虚拟机类加载机制

Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。与其他语言不同,Java语言中,类型的加载、连接和初始化都是在程序运行期间完成的。

7.2 类加载的时机

类的生命周期包括七个阶段,如下所示。

image-20210603212614125

加载,验证,准备,初始化,卸载这五个阶段的顺序是确定的。而解析阶段则不一定,为了支持Java语言的运行时绑定特性,它在某些情况下可以在初始化阶段后开始。

对于“加载”阶段,没有强制的约束。但是对于初始化阶段,有六种情况必须立即“初始化”(而加载、验证、准备自然要在此之前开始):

  1. 遇到new、getatatic、putstatic或invokestatic四条字节码指令时,如果没有进行初始化,则需要先触发初始化,触发场景有:
    • 使用new实例化对象
    • 读取或设置类的静态字段(final修饰或在编译器把结果放入常量池的除外)
    • 调用一个类的静态方法时
  2. 使用java.lang.reflect包的方法对类进行反射调用时,如果类没有初始化,则进行初始化。
  3. 初始化类时,发现其父类还未初始化,则先初始化其父类
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(main方法的类),先初始化这个类

以下两种为jdk7和jdk8的特性。

7.3 类加载的过程

7.3.1 加载

“加载”(Loading)是整个“类加载”(Class Loading)过程中的一个阶段。在加载阶段,Java虚拟机需要完成以下三件事:

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

相对整个类加载过程的其他阶段,非数组类型的加载阶段是开发者可控性最强的阶段。

加载阶段可以用Java虚拟机里内置的引导类加载器来完成,也可以由用户自定义的类加载器去完成,开发者通过定义自己的类加载器去控制字节流的获取方式,实现根据自己的想法来赋予应用程序获取运行代码的动态性。

对于数组有所不同,数组类本身不通过类加载器创建,其由Java虚拟机直接在内存中动态构造。数组类的构造过程遵守以下规则:

  • 如果数组的组件类型是引用类型,则递归采用加载过程去加载这个组件类型,数组C将被标识在加载该组件类型的类加载器的类名称空间上。
  • 如果数组的组件类型不是引用类型(例如int[] 数组的组件类型是int),Java虚拟机将会把数组C标记为与引导类加载器关联。
  • 数组类的可访问性与它的组件类型的可访问性保持一致,如果组件类型不是引用类型,它的数组类的可访问性将默认为public,可被所有的类和接口访问到。

加载阶段后结束后,Java VM外部的二进制字节流则会存储在方法区中,然后在Java堆中实例化一个Java.lang.Class类的对象,这个对象将作为程序访问方法区中的类型数据的外部接口。

7.3.2 验证

验证是连接阶段的第一步,其目的是确保Class文件的字节流符合Java虚拟机规范。

验证包括:

  • 文件格式验证:字节流是否符合CLass文件规范
  • 元数据验证:描述信息是否符合Java语言规范
  • 字节码验证:通过数据流分析和控制流分析,确定程序语义是合法的、符合逻辑的。
  • 符号引用验证:确保解析行为能正常进行,其可以看作对类自身以外的各类信息进行匹配性校验

7.3.3 准备

为类中定义的变量(既静态变量)分配内存并设置类变量初始值的阶段,其都在方法区分配。

7.3.4 解析

该阶段将常量池内的符号引用替换为直接引用的过程。

  • 符号引用:其以一组符号来描述所引用的目标,符号可以是任何形式的字面量。
  • 直接引用:其可以是直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

解析主要针对类、接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符这7类符号引用。

7.3.5 初始化

这里是类加载过程的最后一个步骤。除了加载阶段可以由用户自定义外,其他阶段都由虚拟机主导。

在初始化阶段,会根据程序员的要求去初始化类变量和其他资源。换句话说,初始化阶段就是执行类构造器<clinit>()方法的过程。

  • <clinit>()方法是由编译器自动收集类中的所有类变量的赋值操作和静态语句块中的语句产生的
  • <clinit>()方法不是必需的,如果没有上述的情况则不会生成该方法
作者

bd160jbgm

发布于

2021-06-03

更新于

2021-06-03

许可协议