JVM之虚拟机类加载时机
概述
虚拟机类加载机制就是Java虚拟机把描述类从Class文件加载到内存并对数据进行校验、转换、解析与初始化最终形成可以被虚拟机直接使用的Java类型的过程。
Java语言中,类型的加载、连接和初始化都是在程序运行期间完成的,这会增加一些额外的性能开销,不过这也给Java带来了极大的扩展性和灵活性。
类的加载时机
一个类的生命周期主要为:加载->验证->准备->解析->初始化->使用->卸载 这七个阶段,其中验证、准备、解析这三个阶段有被称为连接阶段。发生的顺序如下图:
其中解析阶段有可能在初始化阶段之后开始(参考java的运行时绑定这一性质)。
主动引用
类加载的时机只有以下六种情况:
遇见new、getstatic、putstatic以及invokestatic这四条字节码指令时候,如果类型没有初始化则需要先进行初始化。
对类型进行反射调用的时候如果类型没有被初始化,则需要先进行初始化。
初始化类的时候,其父类没有被初始化,则先初始化其父类。
虚拟机启动的时候,虚拟机会先初始化主类(包含main()方法的那个类)。
使用jdk7的动态语言支持时,一个java.lang.invoke.MethodHandle实例最后解析的结果为REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四种类型的方法句柄,如果这个句柄的类没有被初始化则先触发初始化。
一个接口定义了JDK8新加入的默认方法(被default关键字修饰的接口方法),在初始化该接口的实现类的时候,需要先初始化该接口。
被动引用
刚刚介绍的六种情况被称为主动引用,而除此之外所有引用类型的方式都不会触发初始化,而这些则被称为被动引用。
例如:
子类引用父类静态字段不会导致子类初始化。
通过数组来定义引用类不会触发此类的初始化。
常量在编译阶段会存入调用类的常量池,本质上没有直接引用到定义常量的类,所以不会触发初始化。
在这插入一点小知识:常量和类以及静态变量是存储在方法区中,而实例对象则是被存储在Java堆中。栈的话存放了基本变量类型和引用对象的变量。
关于接口初始化,其在初始化的时候和类的初始化不同,其并不需要父接口的初始化,只有真正用到了父接口的时候才会初始化。
作者:是xiuxi休息啊
链接:https://juejin.cn/post/7032903220238385165