「JVM 类加载子系统」系列文章

类加载的时机

在类生命周期的七个阶段中,加载、验证、准备、初始化和卸载五个阶段的顺序是确定的,类型的加载过程必须按照这种顺序按部就班地开始,而解析阶段不一定

解析阶段是 Java 虚拟机将常量池内的符号引用替换成直接引用的过程,所以这个过程可能在加载阶段完成,也可能在运行期间完成,这是为了支持 Java 运行时绑定

《Java 虚拟机规范》中并没有强制约束什么情况下必须开始类加载过程的第一个阶段「加载」;但是对于初始化阶段,严格规定了有且只有六种情况必须立即对类进行「初始化」

  1. 遇到newgetstaticputstaticinvokestatic四条字节码指令时,如果类型没有进行初始化,则需要先触发其初始化阶段。能够生成这四条指令的典型 Java 代码场景有:
  1. 使用java.lang.reflect包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化
  2. 当初始化类的时候,发现其父类还没有进行过初始化,则需要先触发其父类的初始化
  3. 当虚拟机启动时,用户需要指定一个要执行的主类 (main() 所在的类),虚拟机会先初始化这个类
  4. 使用 JDK7 新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStaticREF_putStaticREF_invokeStaticREF_newInvokeSpecial四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化
  5. 当一个接口中定义了 JDK8 新加入的默认方法 (default 修饰) 时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化

这六种场景中的行为称为对一个类型进行主动引用;除此之外,所有引用类型的方式都不会触发初始化,称为被动引用

下面是四种被动引用的例子: