虚拟机类加载阶段
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verifcation)、准备(Preparation)、解析(Resolution)、初始化(Initiializtion)、使用(Using)和卸载(Unloading) 7个阶段。
下面简单介绍这7个阶段。
加载
加载是类加载过程的一个阶段,加载阶段他需要完成以下3件事情:
- 通过一个类的全限定名来获得定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法去的这个类的各种数据的访问入口。
在类的生命周期中,开发人员可控性最强的过程就是加载过程。我们可以通过控制类加载器和加载不同class文件,来实现对加载过程的控制。
验证
为了保证程序的可执行和保证程序是无害的,虚拟机在类加载完了之后,有必要对其进行验证。当然,如果确保了类是安全可执行的,这一步也可以通过指定-X
验证阶段主要有四个验证动作:
- 文件格式验证。
主要验证文件格式是否符合要求,例如class文件是否以oxCAFEBAEF开头,版本号时候在有效范围内,常量池是否有不被支持的类型等等。 - 元数据验证。
这一步是对字节码描述的信息记性语义分析,保证其描述符合java语言规范。主要验证内容包括类是否有父类,是否继承了final类;是否不是抽象类,但没有实现其继承的接口或者抽象类的方法。 - 字节码验证。
这一步主要是验证数据流和控制流是合法的,是符合逻辑的。这一个主要验证的内容包括,方法体中的类型转换时有效的,跳转的方法是有效的等等。 - 符号应用验证。
最后一步是校验符号引用是否能转化成直接引用,为下一个阶段的解析做准备。这一步主要校验的内容包括符号应用中通过字符串描述的全限定名是否能找到对应的类;在指定类中是否存在符合房费的字段描述符已经加单名称所描述的方法和字段,符号引用中的类、字段、方法的访问性是否能被当前类访问等等。
准备
准备阶段是正式为类变量分享内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。值得注意的一点是,这里只对其类变量(被static修饰)进行内存申请并是初始化,而成员变量的内存申请和初始化则在类初始化的时候一同进行。
还有一点,这个只对类变量进行初始化,并不对应赋值,例如:
1 | public static int value = 123; |
在准备之后,其初始化的值为0,而不是123,只有staitc final
修饰的常亮才会在准备的时候指定值。例如
1 | public static final int value = 123; |
在准备之后其值就是123了。
解析
解析阶段是虚拟机将常量池内的符号引用替换成直接引用的过程。解析主要包括三种:
- 类或接口的解析。
- 字段解析。
- 类方法解析。
- 接口方法解析。
初始化
类初始化是类加载过程的最后一步,在初始化阶段,才真正开始执行类中定义的java程序代码。
在初始化阶段,会对类变量赋程序给定的值。
<clinit>
方法是由编辑器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,编辑器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在他之后的变量。在前面的静态语句快可以赋值,但是不能访问。
end