JVM类加载的时机

时间:2024-10-07 10:43:48

Java虚拟机(JVM)的类加载机制是Java语言运行时的核心组成部分。它负责将Java类的描述信息从Class文件加载到内存中,并进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。类加载的时机是指触发类加载器去加载一个类到内存中的具体条件。本文将详细介绍JVM类加载的时机。

一、主动引用的触发条件

在JVM中,当程序首次主动使用一个类时,会触发该类的加载和初始化。主动引用的触发条件包括以下几种情况:

  1. 创建类的实例
    当使用new关键字创建类的新实例时,如果该类尚未被加载和初始化,则会触发类加载过程。

  2. 访问类的静态变量或对其进行赋值
    当访问类的静态变量(除了被final修饰并已在编译期赋值的常量外),或者对静态变量进行赋值时,会触发类的加载和初始化。

  3. 调用类的静态方法
    当调用类的静态方法时,同样会触发类的加载和初始化。

  4. 使用反射
    当使用java.lang.reflect包中的方法对类进行反射调用时,如Class.forName()Constructor.newInstance()等,如果类还没有被加载和初始化,则会先触发其加载和初始化。

  5. 初始化子类
    当初始化一个子类时,如果其父类还没有被初始化,则会先触发父类的加载和初始化。

  6. JVM启动时的主类
    JVM启动时,会初始化包含main()方法的那个主类。

  7. 动态代理和动态类型语言支持
    在使用动态代理或JDK 1.7及以后版本引入的动态类型语言支持时,可能会触发相关类的加载和初始化。例如,当一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic等类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化时,需要先触发其加载和初始化。

  8. 接口中的默认方法
    当一个接口中定义了JDK 8新加入的默认方法(被default关键字修饰的接口方法)时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。

二、被动引用的非触发条件

除了上述主动引用的触发条件外,其他引用都不会触发类的加载和初始化,这些被称为被动引用。以下是一些被动引用的例子:

  1. 通过子类的引用访问父类的静态变量
    这种情况下,只会触发父类的加载和初始化,而不会触发子类的加载和初始化。

  2. 通过数组定义引用类
    仅仅通过定义数组来引用类,并不会触发该类的加载和初始化。例如,Parent[] array = new Parent[10]; 这行代码不会触发Parent类的加载和初始化。

  3. 访问类中final修饰的静态常量
    如果常量在编译期间已经存入常量池,那么访问这个常量本质上并没有直接引用到定义该常量的类,因此不会触发该类的加载和初始化。

三、类加载的完整过程

需要注意的是,类加载的完整过程包括加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)和初始化(Initialization)五个阶段。其中,加载、验证、准备三个阶段通常是在初始化之前完成的,并且是由类加载器按照双亲委派模型进行的。类加载机制遵循“懒加载”原则,即只有在真正需要的时候才会加载相应的类。

四、总结

JVM类加载的时机是Java语言运行时的重要特性之一。了解类加载的时机有助于深入理解Java程序的执行过程,并优化程序的性能。在实际开发中,可以根据业务需求和性能要求来选择合适的类加载策略,以实现更高效、更灵活的程序运行。