本文从以下方法介绍类加载过程:
一、类加载时机
二、类加载过程
一、类加载时机
类从加载到虚拟机内存开始,到从虚拟机内存卸载出为止,生命周期如下:
加载(Loading)
验证(Verification)
准备(Preparation)
解析(Resolution)
初始化(Initialization)
使用(Using)
卸载(Unloading)
注意:加载,验证,准备,初始化,卸载,5个阶段的顺序是确定的。
验证、准备、解析阶段也称为连接阶段。
解析阶段的顺序不一定,可能会在初始化阶段之后,这是为了支持java语言的动态绑定。
1、加载
对于类加载过程中的第一个阶段:加载,虚拟机规范中没有明确的规定强制约束加载时机,而是有虚拟机具体实现自由把握。
2、初始化
虚拟机规范明确规定有且只有4中情况立即对类进行初始化(而加载、验证、准备还是要在初始化之前执行)
(1)遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果类未初始化,则立即进行初始化。
(2)使用java.lang.reflact包中的方法对类进行反射调用的时候,如果类未进行初始化,则需要先出发其初始化。
(3)如果初始化一个类时,如果其父类还未进行初始化,则先初始化其父类。
(4)当虚拟机启动时,用户指定一个执行的主类(包含main()方法的类),虚拟机会先初始化该类。
二、类加载过程
类加载的全过程包括加载、验证、准备、解析、初始化五个阶段。
1、加载
“类加载”过程的第一个阶段为“加载”。
加载过程虚拟机执行的操作:
(1)通过类的全限定名(包名+类名)来获取此类的二进制字节类。
(2)将该字节流所代表的静态存储结构转为方法区运行时数据结构。
(3)在堆中生成一个java.lang.Class的字节码文件对象,作为访问方法区数据的入口。
2、验证
为连接阶段的第一步,该步骤的目的是为了确保class文件中包含的信息符合当前虚拟机的要求。
(1)文件格式的验证:
验证字节流是否符合class文件的格式规范,并且能否被当前版本的虚拟机处理。
该阶段的验证部分包括:
①魔数;
②次版本;
③主版本;
④常量池中的常量是否有不被支持的常量类型;
(2)元数据的验证:
对字节码的描述信息进行语义分析,确保描述信息符合java语言的规范要求。
该阶段验证部分如下:
①该类是否有父类,(除了java.lang.Object之外);
②如果有继承关系,父类是否被final修饰;
③如果类不是抽象类或接口,是否实现其父类或接口中的所有方法;
④类中的字段、方法是否与父类产生冲突,如:重写父类中的final方法;
(3)字节码验证
进行数据流和流分析;对类中的方法进行校验和分析。
①保证任意时刻操作数栈中的数据类型与指令码序列都能配合工作;
②保证跳转的指令不会跳转到方法体以外的字节码指令上;
(4)符合引用的验证
校验虚拟机将符号引用转为直接引用的时候信息正确性,该动作在解析阶段发生。
校验内容如下:
①符号引用对应的字符串所表示的全限定名是否能够找到对应的类;
②符号引用中的类、字段、方法的访问性(private...)是否可被当前类访问;
3、准备
准备阶段正式为类变量(static变量)赋初始值,这些内存在方法区中分配。此阶段并未进行类变量的显示初始化赋值。
如:private static int i =123; 该阶段初始值为0而不是123。
特殊情况:
private static int i =123;
javac将i的值生成ConstantValue属性,则再准备阶段虚拟机会根据ConstantValue的设置将i赋值为123。
4、解析
解析阶段虚拟机将常量池中的符号引用直接替换为直接引用的过程。
符号引用:符号引用通过一组符号来描述目标,符号引用可以是任何形式的字面量。符号引用与虚拟机 的内存分布无关,引用的目标不一定需要加载到内存。
直接引用:直接指向目标的指针,相对偏移量或一个能够间接定位目标的句柄。直接引用虚拟机的内存 布局有关,如果有直接引用,则表示目标一定在内存中。
①类或接口的解析
②字段的解析
③类方法的解析
④接口方法的解析
5、初始化
类加载过程中的最后一个阶段,准备阶段已经为类变量进行默认初始化赋值,则该阶段为类变量进行显示初始化赋值。
初始化的阶段是执行类构造器
本文参考自 《深入理解Java虚拟机》 周志明