JVM类加载器

article/2025/9/18 7:13:50

文章目录

  • 一、类加载器
  • 二、类与类加载器
  • 三、双亲委派模型
  • 四、破坏双亲委派模型
    • 4.1、Tomcat
      • 4.1.1、WebApp类加载器
      • 4.1.2、Shared类加载器
      • 4.1.3、Catalina类加载器
      • 4.1.4、Common类加载器
      • 4.1.5、Jsp类加载器
    • 4.2、JDBC

一、类加载器

从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader
在这里插入图片描述

  • 启动类加载器(Bootstrap ClassLoader): 负责加载存放在 <JAVA_HOME>\lib 目录中的核心类库,如rt.jarresources.jar等(或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的类库)。这个加载器是 C++ 编写的,随着JVM启动。

  • 扩展类加载器(Extension ClassLoader): 负责加载<JAVA_HOME>\lib\ext 目录中的类库,(同样也可以用 java.ext.dirs 系统变量来指定路径)。

  • 应用程序类加载器(Application ClassLoader): 负责加载用户类路径 classpath 上所有的 jar 包和 .class 文件。

  • 自定义类加载器: 可以支持一些个性化的扩展功能。


二、类与类加载器

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。


这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

这里所指的“相等”,包括代表类的Class对象的 equals() 方法、isAssignableFrom() 方法、isInstance() 方法的返回结果,也包括使用 instanceof 关键字做对象所属关系判定等情况。


三、双亲委派模型

为了避免类的重复加载,确保一个类的全局唯一性,以及保护程序安全,防止核心API被随意篡改,JVM会采用双亲委派模型进行加载,双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。
在这里插入图片描述
双亲委派模型的工作过程是: 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的夹杂请求最终都应该传送到顶层的启动类加载器中,只有父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。


这里类加载器之间的父子关系一般不会以继承的关系来实现,而是都使用组合关系来复用父加载器的代码,源码如下:
在这里插入图片描述

使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类 java.lang.Object ,它存放在 rt.jar 之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此 Object 类在程序的各种类加载器环境中都是同一个类。

相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为 java.lang.Object 的类,并放在程序的 classPath 中,那系统中将会出现多个不同的 Object 类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。


双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在 java.lang.ClassLoaderloadClass() 方法之中,代码逻辑主要为:先检查是否已经被加载过,若没有加载则调用父加载器的 loadClass() 方法,若父加载器为空,则默认使用启动加载器作为父加载器。如果父类加载器加载失败,抛出 ClassNotFoundException 异常后,在调用自己的 findClass() 方法进行加载。
在这里插入图片描述


四、破坏双亲委派模型

上面我们以及看过了 loadClass() 方法的源码,双亲委派的具体逻辑就实现在这个方法之中,JDK1.2之后就不提倡去覆盖 loadClass() 方法,而是应当把自己的类加载逻辑写到 findClass() 方法中,在 loadClass() 方法的逻辑里如果父类加载失败,则会调用自己的 findClass() 方法来完成加载,这样就可以保证新写出的类加载器是符合双亲委派规则的。


但是双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器实现方式。在Java的世界中大部分的类加载器都遵循这个模型,但也有例外。


4.1、Tomcat

Tomcat为什么会破坏双亲委派模型呢?因为Tomcat需要解决如下问题:

  1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
  2. 部署在同一个Web容器中相同的类库相同的版本可以共享,否则会有重复的类库被加载进JVM。
  3. Web容器也有自己依赖的类库,不能于应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。
  4. Web容器支持jsp文件修改后不用重启,jsp文件也是要编译成 .class 文件的,支持HotSwap功能
    在这里插入图片描述

4.1.1、WebApp类加载器

针对上述问题一,需要Web应用层级的隔离,Tomcat给每个Web应用都创建一个类加载器实例(WebAppClassLoader),该加载器重写了 loadClass() 方法,优先加载当前应用目录下的类,如果当前找不到了,才会一层一层往上找。

4.1.2、Shared类加载器

对于问题二,另外并不是Web应用程序下的所有依赖都需要隔离的,部署在同一个Web容器中相同的类库相同的版本可以共享。否则如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机。

这里Tomcat就在WebAppClassLoader上加了个父类加载器(SharedClassLoader),如果WebAppClassLoader自身没有加载到某个类,那就委托SharedClassLoader去加载。

4.1.3、Catalina类加载器

问题三中需要隔绝Web应用程序与Tomcat本身的类,Tomcat会有类加载器(CatalinaClassLoader)来装载Tomcat本身的依赖。

4.1.4、Common类加载器

如果Tomcat本身的依赖和Web应用还需要共享,那么还有类加载器(CommonClassLoader)来装载进而达到共享。

4.1.5、Jsp类加载器

JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个 .class 文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。 (JSP热部署原理)


4.2、JDBC

双亲委派模型很好地解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载),基础类之所以称为“基础”,是因为他们总是作为被用户代码调用的API,但是如果我们的基础类也要调用用户代码,那该怎么办?


如常见的数据库驱动加载,在使用JDBC写程序之前,通常会调用这行代码Class.forName("com.mysql.jdbc.Driver"),用于加载所需要的驱动类。
在这里插入图片描述
上述代码如果想要运行成功的话,肯定是需要引入mysql-connector-java依赖的,如下:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.45</version>
</dependency>

在引入依赖成功后,我们可以在mysql-connector-java包下查看到的/META-INF/services/java.sql.Driver文件
在这里插入图片描述
其实就是利用了Java SPI机制,文件中内容其实就是JDK中rt.jar类库中的一个接口,如下:
在这里插入图片描述

按照上述介绍的rt.jar类库应该有启动类加载器(Bootstrap ClassLoader)进行加载,但是其实该接口在rt.jar类库是没有任何实现类的,其实现类是在引入的mysql-connector-java包中。
在这里插入图片描述


这里就存在基础类也要调用用户代码,所以启动类加载器(Bootstrap ClassLoader)是无法进行加载的,那么SPI机制下JDBC是如何加载的呢?

有了解Java SPI机制的话,应该知道是使用 ServiceLoader 来加载,这里主要在 DriverManager 类中
在这里插入图片描述

在 DriverManager 类进行加载初始化时,一定会执行 static 代码块
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里可以看出为了解决上述问题,Java引入了一个线程上下文加载器,这个类加载器可以通过 java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,他将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默认就是应用程序类加载器
在这里插入图片描述


http://chatgpt.dhexx.cn/article/lJwEI6pk.shtml

相关文章

自定义类加载器

目录 一、为什么要自定义类加载器&#xff1f; 二、常见的场景 三、实现方式 四、自定义类加载器示例 五、Java9新特性 一、为什么要自定义类加载器&#xff1f; 隔离加载类 在某些框架内进行中间件与应用的模块隔离&#xff0c;把类加载到不同的环境。比如&#xff1a;…

类加载器详解(自己实现类加载器)

目录&#xff1a; java虚拟机汇总 class文件结构分析 1).class文件常量池中的常量项结构 2). 常用的属性表的集合类加载过程 1).类加载器的原理以及实现<< 现在位置虚拟机结构分析 1).jdk1.7和1.8版本的方法区构造变化 2).常量池简单区分对象结构分析 1).压缩指针详解gc…

JVM——自定义类加载器

0. 为什么需要自定义类加载器 网上的大部分自定义类加载器文章&#xff0c;几乎都是贴一段实现代码&#xff0c;然后分析一两句自定义ClassLoader的原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚&#xff0c;因为如果不明白它的作用的情况下&#xff0c;还…

JVM - 类加载器

# 类加载器及类加载器执行过程 JDK版本&#xff1a;1.8 # 1、类加载器子系统 下图为类加载子系统&#xff1a; 类加载子系统负责从文件系统或者网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识&#xff08;CA FE BA BE&#xff09;。Classloader只负责cl…

java的类加载器以及如何自定义类加载器

ClassLoader作用 类加载流程的"加载"阶段是由类加载器完成的。 类加载器结构 结构&#xff1a;BootstrapClassLoader&#xff08;祖父&#xff09;–>ExtClassLoader&#xff08;爷爷&#xff09;–>AppClassLoader(也称为SystemClassLoader)&#xff08;爸…

类加载器(ClassLoader)

一、类加载器&#xff08;ClassLoader&#xff09; 1.1 什么是类加载器 Java的类加载器是Java虚拟机&#xff08;JVM&#xff09;的重要组成部分&#xff0c;它的主要作用是动态地将Java类加载到JVM中&#xff0c;以便在运行时使用这些类。Java类加载器通常是由JVM的实现者提供…

什么是类加载器,类加载器如何分类

一、类加载器 1.什么是类加载器 类加载器&#xff1a;负责将.class文件&#xff08;存储的物理文件&#xff09;加载到内存中 2.类加载时机&#xff1a; ① 创建类的实例&#xff08;对象&#xff09; ② 调用类的实例方法 ③ 访问类或者接口的类变量&#xff0c;或者为该…

深入理解Java虚拟机——再谈类的加载器——第十二章——中篇

深入理解Java虚拟机——Java虚拟机介绍——第一章 深入理解Java虚拟机——类加载子系统——第二章 深入理解Java虚拟机——运行时数据区和本地方法接口——详细篇——第三章 深入理解Java虚拟机——对象的实例化内存布局与访问定位——超级详细篇——第四章 深入理解Java虚拟机…

类加载器详解

类加载器的分类 JVM支持两种类型的类加载器,分别为引导类加载器(BootstrapClassLoader)和自定义类加载器(User-Defined ClassLoader) 从概念上来讲, 自定义类加载器一般指的是程序中由开发人员自定义的一类,类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类…

Java - 类加载器

文章目录 1. 类加载的过程2. 类加载器的分类2.1 引导类加载器 Bootstrap2.2 扩展类加载器 ExtClassLoader2.3 系统类加载器 AppClassLoader2.4 三者之间的关系2.5 自定义类加载器2.6 注&#xff1a;谁来准备类加载器呢? 3. 双亲委派机制4. ClassLoader抽象类5. URLClassLoader…

DAU 和 MAU

DAU 和 MAU 日活跃用户占月活跃用户的比例越高&#xff0c;表明用户对App的使用粘性越高。 DAU&#xff0c;即&#xff1a;Daily Active User&#xff0c;指日活跃用户数 MAU&#xff0c;即&#xff1a;Monthly Active User&#xff0c;指月活跃用户数。 *例子1&#xff1a;…

如何通过DAU分析活跃用户?(案例:python绘制箱体图)

前言&#xff1a;本文内容以游戏产品为基础进行讲解&#xff0c;内容为以下4部分&#xff1a; 1. 如何理解DAU反映了哪些问题&#xff1f; 2. 有哪些因素会影响DAU变动&#xff1f; 3. 如何解读DAU的“箱体图”&#xff1f; 4. 如何使用python绘制“箱体图”&#xff1f; DAU的…

【数据分析】产品日活DAU下降,怎么分析

目录 案例简介 第一步&#xff1a;确认数据真实性 第二步&#xff1a;明确定义&#xff0c;并拆解指标&#xff0c;进一步定位原异常部分 第三步&#xff1a;根据几个常见维度初步拆分数据 第四步&#xff1a;进一步做假设并细分深入&#xff0c;得出结论 案例分析 例题…

数据分析——DAU下降问题(转)

文章转自&#xff1a; DAU异常下降该如何分析 1. 梳理公司的用户增长模式 尽管不同业务形态、以及不同发展阶段的公司&#xff0c;其用户增长模式各有差异&#xff0c;但都可以从拉新策略和促活策略进行分解。 常见的拉新策略有&#xff1a; 流量采购。比如通过厂商预装、…

有赞一面:亿级用户DAU日活统计,有几种方案?

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如极兔、有赞、希音、百度、网易、滴滴的面试资格&#xff0c;遇到一几个很重要的面试题&#xff1a; (1) 亿级用户场景&#xff0c;如何高性能统计日活&#xff1f; (2) 如何实现亿…

数据分析——用户粘性指标 DAU/MAU

&#xff08; 一 &#xff09;定义 DAU&#xff0c;即&#xff1a;Daily Active User&#xff0c;指日活跃用户数&#xff1b; MAU&#xff0c;即&#xff1a;Monthly Active User&#xff0c;指月活跃用户数。 日活/月活就是体现用户粘性最频繁使用的指标。日活跃用户占月活…

数据分析体系 - 用户粘性(DAU/MAU 和 月人均活跃天数)

对于常见的App&#xff0c;用户粘性的取值范围就是3%~100%&#xff0c; 不同领域的App也会有不同的基准值&#xff0c; 例如移动游戏会以20%为基线&#xff0c; 而工具类App会以40%为基线。 用户粘性的两个计算指标&#xff1a; 1、DAU/MAU 2、月平均活跃人数 这里其实…

数据分析——DAU下降/上升原因分析

本文是对“DAU变动原因”问题进行的思考整理&#xff0c;仅作记录&#xff0c;欢迎讨论。 &#xff08; 一 &#xff09;思维框架 图版&#xff1a; 文版&#xff1a; 内部原因&#xff1a; 1. 数据验证 如果DAU上升或者下降&#xff0c;且非日常波动&#xff0c;需先确…

数据分析 — 用户粘性的两个计算指标(DAU/MAU和月人均活跃天数)

很多运营都了解DAU&#xff08;日活跃用户数&#xff09;和MAU&#xff08;月活跃用户数&#xff09;的重要性&#xff0c;但在某些情况下这两个数值本身并不能反映出太多问题&#xff0c;这个时候就要引用到【DAU/MAU】的概念&#xff0c;即【日活/月活】。 用户粘性的两个计…

峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?

小游戏《羊了个羊》 短短的7天内&#xff0c;DAU突破了1亿、吞吐量峰值21WQps。 《羊了个羊》运营后台数据显示&#xff0c;在短短的7天内&#xff0c;这款小游戏的DAU就突破了1亿。 要知道&#xff0c;除了王者荣耀、原神等屈指可数的现象级手游之外&#xff0c;1亿DAU是这个…