Java中类加载的机制
从宏观上看:
加载->链接->初始化
整个生命周期流程:
加载->验证->准备->解析->初始化->使用->卸载
1、加载
查找并加载类的二进制数据,并生成Class对象的实例的过程
也就是将类的.class文件加载到 JVM中
- 通过类的全限定类名,获取类的二进制数据流;
- 解析类的二进制数据流转换为方法区内的数据结构;
- 创建java.lang.Class类型的对象,作为方法区这个类的各种数据的访问入口
2、链接
在链接阶段,Java类加载器对类进行验证、准备和解析操作。将类与类的关系(符号引用转为直接引用)确定好,校验字节码
-
验证:校验类的正确性(文件格式,元数据,字节码,二进制兼容性),保证类的结构符合JVM规范。
-
准备:为类变量分配内存并设置类变量的默认初始值,这些变量使用的内存都在方法区中分配。(这里初始化
的是类变量,即static字段,实例变量会在对象实例化时随对象一起分配在Java堆中。) -
解析︰把类的符号引用转为直接引用(类或接口、字段、类方法、接口方法、方法类型、方法句柄和访问控制
修饰符7类符号引用)
3、初始化
初始化是类加载的最后一步,也是真正执行类中定义的Java程序代码(字节码),初始化阶段是执行类构造器<clinit> ()
方法的过程。这里利用了一种懒加载的思想,所有Java虚拟机实现必须在每个类或接口被Java程序首次主动使用才初始化,但类加载不一定,静态代码块在类初始化时执行
- 当遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,比如new一个类,读取一个静态字段(未被final 修饰)、或调用一个类的静态方法时会进行类的初始化
- 使用java.lang.reflect包的方法对类进行反射调用时,如果类没初始化,需要触发其初始化
- 初始化一个类,如果其父类还未初始化,则先触发该父类的初始化
- 当虚拟机启动时,用户需要定义一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个类
- 当使用JDK1.7的动态动态语言时,如果一个MethodHandle实例的最后解析结构为REF_getStatic、
REF_putStatic、REF_invokeStatic、的方法句柄,并且这个句柄没有初始化,则需要先触发器初始化