一马平川
不积跬步无以至千里,后继才能薄发

JVM类加载过程学习笔记

2021年06月20日
0
未分类

JVM类加载过程

什么是类加载?

在代码编译后,就会生成JVM(Java虚拟机)能够识别的二进制字节流文件(*.class)。而JVM把Class文件中的类描述数据从文件加载到内存,并对数据进行校验、转换解析、初始化,使这些数据最终成为可以被JVM直接使用的Java类型,这个说来简单但实际复杂的过程叫做JVM的类加载机制。

类加载过程分别是什么?

Untitled

  1. Class类文件加载

    首先类加载器通过一个类的全限定名获取此类的二进制字节流,将字节流中的静态存储结构转化为运行时方法区的数据结构,然后在内存中生成该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口,就完成了Class类文件的加载

  2. 验证

    首先会检查二进制字节流合法性

    然后校验类型:

    • 文件格式验证
    • 元数据验证
    • 字节码验证:检查局部变量表中slot是否越界使用
    • 符号引用验证:符号引用校验发生在解析阶段,符号引用可以看做是类自身以外的的信息进行匹配性校验,其中包含了验证符号引用中的全限定名能否找到对应的类、验证指定类中是否有存在符号方法的字段描述符,以及简单名称所描述的方法和字段、验证符号引用中的类、字段、方法的访问性,能否被当前类访问
  3. 准备

    首先为对象分配内存,然后设置类变量初始值,如int的0,boolean的false,若字段表中存在的ConstantValue,则对应的变量会提前初始化为设定值(若为final变量,提前读取常量池获取值)

  4. 解析

    将符号引用转化为直接引用

  5. 初始化

    触发类初始化的场景:

    • 遇到new、getstatic、setstatic、invokestatic,这四条字节码指令,如果类没有初始化,则要先触发初始化。使用new关键字、读取或设置一个类的静态字段(被final修饰、已在编译期就把结果放入常量池的静态字段除外),以及调用静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用的时候
    • 初始化一个类的时候,如果父类还没有初始化,则需要先触发其父类的初始化
    • 当虚拟机启动时,用户指定的主类需要先初始化
    • 使用JDK1.7的动态语言支持,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄的对应的类没有初始化,需要触发其初始化

    什么情况下不会触发初始化?

    • 调用父类的static域的变量、方法,不会触发子类的初始化
    • 创建一个类的数组,不会触发初始化
    • 编译阶段的常量传播优化:在常量在编译阶段会存入调用类的常量池中
  6. 使用

    我们在代码中声明创建之后,进行使用

  7. 卸载

    卸载分为两种情况:

    1. jvm自带的类加载器:由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。前面已经介绍过,Java虚拟机自带的类加载器包括根类加载器、扩展类加载器和系统类加载器。Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的。
    2. 用户自定义的类加载器:由用户自定义的类加载器所加载的类是可以被卸载的

如果喜欢这篇文章,可以给作者评个份哦~

原文声明: "转载本站文章请注明作者和出处Nothinglin ,请勿用于任何商业用途"

公众号:苦逼的学生仔