《深入理解java虚拟机》笔记 —— 第七章 虚拟机类加载机制

时间:2023-01-02 13:56:14

第七章

  • 概述
    虚拟机把描述类的数据从Class文件加载到内存,对数据进行校验,转换解析,初始化,最终形成可以被java虚拟机直接使用的java类型,这就是类加载机制。
  • 类加载时机
    类从被加载到内存中到卸载出内存,所经过的生命周期为:加载-验证-准备-解析-初始化-使用-卸载,其中验证-准备-解析三个部分统称为连接。
    解析阶段和其他的阶段不同,解析阶段可以在初始化之后执行,这时为了支持java语言的运行时绑定(动态绑定)。
    什么时候需要开始初始化?有且只有5种情况如下:
    • 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
    • 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
    • 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其类的初始化。
    • 当虚拟机启动时,用户需要指定一个需要执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类。
    • 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后解析的结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,那么需要先触发初始化。
      需要注意的是:
      • 通过子类引用父类的静态字段,不会导致子类初始化
      • 通过数组定义来引用来,不会触发此类初始化
      • 常量在变异阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
  • 类加载的过程
    在加载阶段,虚拟机需要完成一下三件事情

    • 通过一个类的全限定名来获取定义此类的二进制字节流
    • 将这个字节流所代表的静态存储结构转化为方法区运行时数据结构。
    • 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

    对于数组类的创建而言,数组类不是通过类加载器创建,它是由java虚拟机直接创建的。但数组类与类加载器仍有很密切的关系,因为数组类的元素类型最终是要靠类加载器去创建,一个数组类加载过程遵循以下规则:

    • 如果数组的组件类型是引用类型,那就递归采用本节中定义的加载过程去加载这个组件类型,数组C将在加载该组件类型的类加载器的类名称空间上被标识。
    • 如果数组的组件类型不是引用类型,java虚拟机将会把数组C标记为与引导类加载器