天天看点

彻底弄懂类加载和JVM的双亲委派机制

类加载

类的生命周期会经历以下 7 个阶段:

  1. 加载阶段(Loading)
  2. 验证阶段(Verification)
  3. 准备阶段(Preparation)
  4. 解析阶段(Resolution)
  5. 初始化阶段(Initialization)
  6. 使用阶段(Using)
  7. 卸载阶段(Unloading)

其中验证、准备、解析 3 个阶段统称为连接(Linking),如下图所示

彻底弄懂类加载和JVM的双亲委派机制

JVM 类加载通常指的就是前五个阶段:加载、验证、准备、解析、初始化

1.加载阶段

此阶段用于查到相应的类(通过类名进行查找)并将此类的字节流转换为方法区运行时的数据结构,然后再在内存中生成一个能代表此类的 java.lang.Class 对象,作为其他数据访问的入口。

2.验证阶段

此步骤主要是为了验证字节码的安全性,如果不做安全校验的话可能会载入非安全或有错误的字节码,从而导致系统崩溃,它是 JVM 自我保护的一项重要举措。

验证的主要动作大概有以下几个:

  • 文件格式校验包括常量池中的常量类型、Class 文件的各个部分是否被删除或被追加了其他信息等;
  • 元数据校验包括父类正确性校验(检查父类是否有被 final 修饰)、抽象类校验等;
  • 字节码校验,此步骤最为关键和复杂,主要用于校验程序中的语义是否合法且符合逻辑;
  • 符号引用校验,对类自身以外比如常量池中的各种符号引用的信息进行匹配性校验。

3.准备阶段

此阶段是用来初始化并为类中定义的静态变量分配内存的,这些静态变量会被分配到方法区上。

HotSpot 虚拟机在 JDK 1.7 之前都在方法区,而 JDK 1.8 之后此变量会随着类对象一起存放到 Java 堆中。

4.解析阶段

此阶段主要是用来解析类、接口、字段及方法的,解析时会把符号引用替换成直接引用。

所谓的符号引用是指以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可;而直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

符号引用和直接引用有一个重要的区别:使用符号引用时被引用的目标不一定已经加载到内存中;而使用直接引用时,引用的目标必定已经存在虚拟机的内存中了。

5.初始化

初始化阶段 JVM 就正式开始执行类中编写的 Java 业务代码了。到这一步骤之后,类的加载过程就算正式完成了。

JVM的双亲委派机制

Java类加载并非单纯按照类逐个进行加载,我们知道有通常有多个父类和多个子类,面对复杂的类,那如何保证类加载的有序性和安全性呢?

这就是本文要讲到的重点JVM的双亲委派机制。

在讲双亲委派机制之前,我们需要先了解一下JVM类加载器。

JVM类加载器有三种:

  1. 启动类加载器
  2. 扩展类加载器
  3. 应用程序类加载器

除以上三种外,我们还可以自定义类加载器。

启动类加载器

负责加载Java_HOME目录下的lib中的类库,是jdk的核心类库,同时我们也可以通过-Xbootclasspath参数指定路径中被虚拟机认可的类库。

扩展类加载器

负责加载Java_HOME中lib子下ext子目录中的类库,也就是加载JDK的扩展类库,同时我们可以通过java.ext.dirs系统参数变量加载指定路径中的类库。

应用程序类加载器

负责加载用户路径classpath上的类库

另外, 我们也可以通过继承java.lang.ClassLoader,根据不同需求实现自定义类的加载器。

向上委派机制

向上委派机制指一个类在收到类加载请求后, 不会尝试自己加载这个类,而是把这个类加载请求向上委派给他的父类去完成,父类在接收到这个类加载请求后,又委派给他的父类,依次类推,这样的话,所有的类加载请求都被向上委派到启动类加载器中,这样的委派机制叫做向上委派机制。

向上委派的过程分为下图中3步

彻底弄懂类加载和JVM的双亲委派机制

向下委派机制

当父类加载器接收到类加载请求后,发现自己也无法加载这个类,出现这种情况,通常是这个类的Class文件在父类的类加载路径中不存在,这时,父类会将这个信息反馈给子类,并向下委派子类加载器加载这个类,知道他被成功加载,如果这时仍然找不到这个类,那么JVM就会抛出ClassNotFoud异常。

向下委派类加载过程,分为下图中的四步

彻底弄懂类加载和JVM的双亲委派机制

如果最终在自定义类路径下仍找不到目标Class文件,那么JVM就会抛出ClassNotFoud异常,类加载过程失败,JVM也会启动失败。

双亲委派中向上委派保证了先加载JDK的核心类,再加载应用程序的类,有效防止了某个应用程序中因为某个类存在的一些不安全问题而导致JVM变得不安全,而从上向下的委派过程则保障了需要加载的类,都得到了加载。如果从上到下仍然在自定义类路径下仍找不到目标Class文件,那么JVM就会抛出ClassNotFoud异常,类加载过程失败,JVM也会启动失败。如果类名和包名一样也无法完成类加载。

彻底弄懂类加载和JVM的双亲委派机制

声明:本文为极客时间每日一课学习做的笔记。