1 加载(Loading)
1、通过一个类的全限定名获取定义此类的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
4、类的加载由类加载器来实现。类加载分为四种,根类加载器(引导类加载器),扩展类加载器,系统类加载器,用户自定义类加载器。
2 链接(Linking)
分为三个子阶段,验证,准备,解析。
2.1 验证(Verification)
1、目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性。
2、主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
(1)文件格式验证,是否以魔术oxCAFEBABE开头,主版本和副版本是否在当前Java虚拟机的支持范围内,数据中每一项是否都拥有正确的长度等。
(2)元数据验证,是否继承final,是否有父类,抽象方法是否有实现。
(3)字节码验证,跳转指令是否指向正确位置,操作数类型是否合理。
(4)符号引用验证,符号引用的直接引用是否存在
2.2 准备(Preparation)
1、为类变量分配内存并且设置该类变量的默认初始值,即零值。
类型 | 默认初始值 |
---|---|
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0 |
char | \u0000 |
boolean | false |
reference | null |
2、不包含用final修饰的static,因为final在编译的时候就会分配,准备阶段会显式初始化。
3、不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
2.3 解析(Resolution)
1、将常量池内的符号引用转换为直接引用。
2、解析操作往往会伴随着JVM在执行为初始化之后再执行。
3、符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java虚拟机规范》的Class文件格式中,直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。
4、解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对于常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
3 初始化(Initization)
1、初始化阶段就是执行类构造器方法clinit()的过程。
2、clinit()方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来,该类必须要有静态变量并赋值才会执行clinit()方法。
3、clinit()方法按语句在源文件中出现的顺序执行。
4、clinit()方法不同于类的构造器方法,构造器方法是虚拟机视角下的init()方法。
5、若该类具有父类,JVM会保证子类的clinit()方法执行前,父类的clinit()方法已经执行完毕。
6、不需要显式调用父类的clinit()方法(接口除外,接口不需要调用父接口的初始化方法,只有使用到父接口中的静态变量
时才需要调用)
7、虚拟机必须保证一个类的clinit()方法在多线程下被同步加锁。