类的生命周期
一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历 加载(loading)、验证(Verfication)、准备(Preparation)、解析(Resolution)、初始化(initialization)、使用(Using)、卸载(Unloading)。
其中 验证(Verfication)、准备(Preparation)、解析(Resolution) 称为连接。
加载
关于什么情况下开始类加载的第一个阶段“加载”,《Java虚拟机规范》中并没有进行强制约束,可以交给具体实现自由把握。加载阶段需要完成的三件事:
1.通过类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3.在内存中生成一个代表这个类的java.lang.class对象,作为方法去这个类的各种数据的访问入口。
验证
准备
准备阶段是为类变量分配内存并设置类变量初始值值的阶段。
即这时候进行内存分配的仅包括类变量(静态变量,被 static 关键字修饰的变量,只与类相关,因此被称为类变量)。
初始值"通常情况"下是数据类型默认的零值(如 0、0L、 null、false 等),特殊情况:比如给变量加上了 final 关键字如:public static final int value=111 ,那么准备阶段 value的值时111。
{
public static int value=111 // 准备阶段初始值是0
public static final int value=111 //准备阶段 value的值时111
}
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
初始化
《Java虚拟机规范》中严格规定六种情况必须立即对类进行初始化:
1. 遇到 new、getstatic、putstatic、invokestatic 这四条字节指令时,没有初始化需要先触发初始化阶段。使用场景:
- 使用new关键字实例化对象
- 读取或设置一个类型的静态字段,被final修饰、已在编译器把结果存放如常量池的静态字段除外。
- 使用一个类的静态方法的时候
2. 使用java.lang.reflect包的方法对类进行反射调用的时候,没有初始化需要先触发初始化阶段
3. 初始化类的时候父类没有初始化需要先触发父类初始化阶段
4.当虚拟机启动的时候,虚拟机会先初始化主类(用户指定的一个要执行的主类,即包含main()方法的那个类)。
5. 当使用jdk7新加入的动态语言支持的时候,如果一个java,lang.invoke.MethodHandler实例的最后解析结果是REF_getStatic,REF_putStatic,REF_invokeStatic,REF_newInvokeSpecial四种类的方法句柄,并且这个方法句柄对应的类没有进行过初始化,那么需要先触发其初始化。
6. 当一个接口中定义了JDK8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那么该接口要在其之前初始化。