第7章-虚拟机类加载机制

本章介绍了类加载过程的”加载“、”准备“、”验证“、”解析“和”初始化“5个阶段中虚拟机进行了哪些动作,还介绍了类加载器的工作原理及其对虚拟机的意义。

类加载的时机

类从被加载到虚拟机内存中开始,到卸载出内存为止。
生命周期:加载验证准备、解析、初始化、使用和卸载7个阶段(粗体5个顺序是确定的)。
Alt text
主动引用:

  • 遇到new、getstatic、putstaic或invokestatic这4条指令
  • 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没进行初始化,则需先触发其初始化。
  • 当初始化一个类时,如果发现其父类还没有进行初始化,则需先解决父类的初始化。
  • 当虚拟机启动时,用户需要指定一个要执行的类(包含main()方法的那个类),虚拟机会先初始化这个主类
  • 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。

被动引用:

  • 对于静态字段,只有直接定义这个字段的类才会被初始化
  • 通过数组定义来引用类,不会触发类的初始化
  • 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。

类加载的过程

加载

类加载的第一个阶段。
过程:

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

验证

确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
过程:

  1. 文件格式验证(检查Class文件格式规范)
  2. 元数据验证(检查语义合法性、逻辑性)
  3. 字节码验证(最复杂的阶段)
  4. 符号引用验证(确保解析动作能正常执行)

准备

是正式为类变量分配内存并设置类变量初始化值的阶段。
重点:

  1. 这时进行内存分配的仅包括类变量(被static修饰的变量),不包括实例变量
  2. 默认变量初始化值(常量从常量池取对应的常量值)

解析

是虚拟机将常量池内的符号引用替换为直接引用的过程。

  1. 符号引用:符号引用以一组符号来描述所有引用的目标,符号可以是任何形式的字面量。
  2. 直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
  3. 解析对象(7类符号引用):
  • 类或接口
  • 字段
  • 类方法
  • 接口方法
  • 方法类型
  • 方法句柄
  • 调用点限定符。
  1. 类或接口的解析(3个步骤)
  • 如果C不是一个数组类型,那虚拟机将会把代表N的全限定名传递给D的类加载器去加载这个类C。
  • 如果C是一个数组类型,并且数组的元素类型为对象,也就是N的描述符会是类似“[Ljava/lang/Inter”的形式,按上面的规则加载数组元素类型
  • 如果上面解析没问题,在解析完成前不要进行符号引用验证,确认D是否具备对C的访问权限。
  1. 字段解析
    Alt text
    Alt text

  2. 类方法解析
    Alt text

  3. 接口方法解析
    Alt text

初始化

真正开始执行类中定义的Java程序代码,执行类构造器<cinit>()方法的过程。
过程:
Alt text

类加载器

“通过一个类的全限定名来获取描述此类的二进制流”这个动作放到Java虚拟机外部去实现,实现这个动作的代码模块称为“类加载器”。

类与类加载器

用于实现类的加载动作。
例:两个类是否相等(前提是被同一个类加载器加载的)
Alt text

双亲委派模型

类加载器粗分2类:

  • 启动类加载器(BootstrapClassLoader)
  • 所有其他类的加载器(java.class)

细分3类:

  • 启动类加载器(Bootstrap ClassLoader)
  • 扩展类加载器(Extension ClassLoader)
  • 启动程序类加载器(ApplicationClassLoader)
    Alt text

过程:
Alt text
优点:Java类随着它的类加载器一起具备了一种带有优先级的层次关系
代码实现:
Alt text

破坏双亲委派模型

loadClass()失败->findClass()–JDK1.2后添加一个新的方法,为了兼容1.2之间的代码。

线程上下文类加载器

产生原因:基础类调回用户的代码(如:JNDI服务),Java中所有涉及SPI的加载动作基本上都采用这种方式
Alt text

OSGI环境

Alt text