【JVM系列】虚拟机类加载机制

【JVM系列】虚拟机类加载机制

【1】类加载的时机

类的生命周期如下图所示;

【JVM系列】虚拟机类加载机制_第1张图片

类初始化开始的时机(主动引用):

1. 遇到 new,getstatic,putstatic,invokestatic 字节码时,需要初始化;

2. 使用 java.lang.reflect 包的方法对类进行反射调用的时,需要初始化;

3. 初始化类时,若该类的父类未初始化则先触发其父类的初始化;(注:对于类初始化要求其父类全部都已经初始化,对于接口初始化只有当真正用到父接口时才会对父接口初始化);

4. 虚拟机启动时,需要指定待执行的主类,虚拟机优先初始化该类;

5. JDK 动态语言支持时,若 java.lang.invoke.MethodHandle 实例最终解析结果是 REF_getStatic,REF_putStatic,REF_invokeStatic 的方法句柄,并且方法句柄对应的类没有初始化,则触发初始化;

被动引用(引用类但不会触发初始化处理),场景:

1. 通过子类引用父类的静态字段,不会导致子类初始化,对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过子类引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化;

2. 通过数组定义引用类,不会触发此类的初始化;

3. 常量在编译阶段会存入调用常量类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化;

【2】类加载的过程

加载阶段

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

数组的特殊性

数组类本身不通过类加载器创建,其由 Java 虚拟机直接创建,数组的元素类型需要类加载器创建;

数组类创建过程规则

1. 数组的组件类型为引用类型,递归加载该组件类型,数组在加载该组件类型的类加载器的类名称空间上被标识;

2. 数组的组件类型不是引用类型,Java虚拟机会将数组标记为与引导类加载器关联;

3. 数组类的可见性与其组件类型的可见性一致,若组件类型不是引用类型,则数组类的可见性默认为public;

 

验证阶段

目的:确保Class文件的字节流中包含的信息符合当前虚拟机的要求;

阶段性检验动作:1. 文件格式验证;2. 元数据验证;3. 字节码验证;4. 符号引用验证;

 

准备阶段

作用:正式为类变量分配内存并设置类变量初始值,该阶段变量所用的内存将在方法区中进行分配,该阶段类变量初始值为零;当类字段的字段属性存在ConstantValue属性,则准备阶段变量将初始化为ConstantValue属性所指定的值;

 

解析阶段

作用:将常量池内的符号引用替换为直接引用;

符号引用,以一组符号描述所引用的目标,符号可以是任意形式的字面量,只要使用时能够无歧义地定位到目标即可;

直接引用,直接指向目标的指针、相对偏移量、间接定位到目标的句柄,直接引用与虚拟机实现的内存相关;

解析发生时机,发生在执行 anewarray,checkcast,getfield,getstatic,instanceof,invokedynamic,invokeinterface,invokespecial,invokestatic,invokevirtual,ldc,ldc_w,multianewarray,new,putfield,putstatic字节指令之前;

解析针对的符号与常量类型

类或接口 字段 类方法 接口方法 方法类型 方法句柄 调用点限定符

CONSTANT_

Class_info

CONSTANT_

Fieldref_info

CONSTANT_

Methodref_info

CONSTANT_

InterfaceMethodref_info

CONSTANT_

MethodType_info

CONSTANT_

MethodHandle_info

CONSTANT_

InvokeDynamic_info

D:当前代码所处的类;N:从未解析过的符号引用;C:类或接口的直接引用;

类或接口解析步骤:

【JVM系列】虚拟机类加载机制_第2张图片

字段解析步骤:

【JVM系列】虚拟机类加载机制_第3张图片

类方法解析步骤:

【JVM系列】虚拟机类加载机制_第4张图片

接口方法解析步骤:

【JVM系列】虚拟机类加载机制_第5张图片

初始化阶段

作用:根据程序员通过程序制定的主观计划初始化类变量和其他资源,即初始化阶段是执行类构造器()方法的过程;

()方法对程序行为的影响:

1. ()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块只能访问到定义在其之前的变量,定义在其之后的变量,在前面的静态语句块可以赋值但不能访问;

2. 虚拟机保证在子类的()方法执行之前,父类的()方法已经执行完毕;

3. 父类中定义的静态语句块要优先于子类的变量赋值操作;

4. ()方法对于类或接口并不是必须的;

5. 接口与类一样都会生成()方法(接口中不能生成静态语句块,但仍然有变量初始化的赋值操作),并且执行接口的()方法不需要优先于父接口的()方法;

6. 虚拟机保证一个类的()方法在多线程环境中被正确地加锁、同步;

 

【3】类加载器

作用:通过一个类的全限定名来获取描述此类的二进制字节流;

类与类加载器:对于任意的一个类,需要由加载它的类加载器和这个类本身一同确定其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间;

双亲委派模型:

类加载器的分类,1. 启动类加载器,负责将存放在\lib目录中的,或被-Xbootclasspath参数所指定的路径中的,并且是虚拟机能够识别的类库加载到虚拟机内存中;2. 扩展类加载器,加载\lib\ext目录中的,或者被java.ext.dirs 系统变量所指定的路径中的所有类库;3. 应用程序类加载器,加载用户类路径上所指定的类库;

类加载器之间的层次结构(双亲委派模型),双亲委派模型要求除顶层的启动类加载器之外,其余的类加载器都应当有自己的父类加载器,子类加载器以组合的方式复用父加载器的代码;

双亲委派模型优点:Java类随着它的类加载器一起具备了一种带有优先级的层次关系;

【JVM系列】虚拟机类加载机制_第6张图片

参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】深入理解 Java 虚拟机 JVM 高级特性与最佳实践

【2】Java虚拟机类加载机制和双亲委派模型

你可能感兴趣的:(JVM)