JVM 触发类加载的条件有哪些?

目录

一、类加载生命周期

二、主动引用

2.1、创建类的实例

2.2、访问类的静态字段或静态方法

2.3、反射

2.4、初始化类的子类时,先初始化父类

[2.5、虚拟机启动时,初始化 main 方法所在的类](#2.5、虚拟机启动时,初始化 main 方法所在的类)

2.6、动态语言支持

三、被动引用

3.1、通过子类引用父类的静态字段

3.2、访问编译期常量

3.3、通过数组定义类引用


Java 虚拟机(JVM)中,类的加载并不是随意发生的,而是由特定的触发条件 决定的。什么时候加载?什么时候初始化?

这是我们必须要搞清楚的问题,尤其在复杂的应用中,弄懂类加载的时机能帮助我们避免一些潜在的性能问题和运行时错误。

在本节中,我们将详细探讨类加载的时机、主动和被动引用的区别,以及常见的类加载触发条件。

一、类加载生命周期

类加载的生命周期包括:加载(Loading)链接(Linking)初始化(Initialization) 。而其中,初始化阶段是决定类是否被真正加载的关键。

JVM 在什么时候启动类加载过程呢?

主要分为主动引用被动引用两种情况。我们分别看看这两种情况在什么条件下会触发类加载。

二、主动引用

主动引用 是指程序显式地使用某个类,从而触发类的加载和初始化。根据《Java 虚拟机规范》,以下六种情况会触发类的主动引用,也就是触发类加载的条件!

2.1、创建类的实例

当你使用 new 关键字创建一个类的实例时,JVM 会立即加载并初始化该类。

java 复制代码
// 触发 MyClass 的加载和初始化
MyClass obj = new MyClass(); 

初始化流程

  1. 分配内存给 MyClass 的实例对象。

  2. 加载 MyClass 类的字节码,并执行静态代码块和静态变量赋值操作。

2.2、访问类的静态字段或静态方法

访问类的静态字段或静态方法时,也会触发类的加载和初始化。

java 复制代码
// 触发 MyClass 的加载
System.out.println(MyClass.staticVar);  
// 触发 MyClass 的加载
MyClass.staticMethod();                

常量不会触发类加载 :如果静态字段是 final 修饰的常量,它在编译期已存入常量池,因此不会触发类加载。

java 复制代码
System.out.println(MyClass.FINAL_CONSTANT);  // 不触发类加载

2.3、反射

通过反射调用类时,也会触发类加载。

java 复制代码
Class<?> clazz = Class.forName("com.example.MyClass");  // 触发 MyClass 的加载

2.4、初始化类的子类时,先初始化父类

当初始化一个类时,如果它的父类尚未初始化,JVM 会先初始化父类。

java 复制代码
public class Parent {
    static {
        System.out.println("父类初始化");
    }
}

public class Child extends Parent {
    static {
        System.out.println("子类初始化");
    }
}

// 先输出"父类初始化",再输出"子类初始化"
Child obj = new Child();  

2.5、虚拟机启动时,初始化 main 方法所在的类

虚拟机启动时,main 方法所在的类是程序的入口类,会被优先加载和初始化。

java 复制代码
public static void main(String[] args) {
    System.out.println("主类加载");
}

2.6、动态语言支持

在 Java 7 引入的 java.lang.invoke 包中,当 MethodHandle 最终指向的类需要初始化时,也会触发类的加载。

java 复制代码
MethodHandle handle = MethodHandles.lookup().findStatic(MyClass.class, "staticMethod", MethodType.methodType(void.class));
handle.invoke();  // 可能触发 MyClass 的加载

三、被动引用

被动引用不触发类加载。

与主动引用相对,被动引用是指访问类的某些特性时不会触发类的加载和初始化。以下是几种典型的被动引用场景。

3.1、通过子类引用父类的静态字段

如果子类只引用父类的静态字段,JVM 只会初始化父类,而不会初始化子类。

示例

java 复制代码
// 只触发 Parent 的加载,不触发 Child 的加载
System.out.println(Child.staticVar);  

3.2、访问编译期常量

访问 final 修饰的编译期常量,不会触发类的加载。

java 复制代码
// 不触发 MyClass 的加载
System.out.println(MyClass.FINAL_CONSTANT);  

3.3、通过数组定义类引用

通过数组引用一个类,不会触发该类的加载。

java 复制代码
// 不触发 MyClass 的加载
MyClass[] array = new MyClass[10];  

最后,为什么需要关注类加载的时机?

  • 避免类的过早加载:过早加载可能导致不必要的内存消耗,尤其在大型应用中。

  • 延迟加载(Lazy Loading):通过延迟加载,可以在真正需要时才加载类,减少启动时间。

  • 减少类加载冲突:在模块化或插件化的应用中,合理安排类加载顺序有助于避免类冲突和类加载死锁问题。

相关推荐
yejqvow122 小时前
CSS如何控制placeholder文字的颜色_使用--placeholder伪元素
jvm·数据库·python
m0_743623922 小时前
HTML怎么创建多语言切换器_HTML语言选择下拉结构【指南】
jvm·数据库·python
pele2 小时前
Angular 表单中基于下拉选择动态启用字段必填校验的完整实现
jvm·数据库·python
HHHHH1010HHHHH2 小时前
Redis怎样判断节点是否主观下线_哨兵基于down-after-milliseconds参数的心跳超时判定
jvm·数据库·python
qq_654366983 小时前
CSS如何处理@import样式表的嵌套加载_评估递归对加载的影响
jvm·数据库·python
weixin_381288183 小时前
苹果微软双修党福音:Navicat如何跨系统平滑迁移配置
jvm·数据库·python
qq_342295824 小时前
CSS如何制作点击展开时的手风琴动画_平滑过渡max-height高度
jvm·数据库·python
m0_748920364 小时前
Golang goquery怎么解析HTML_Golang goquery教程【核心】
jvm·数据库·python
m0_746752304 小时前
golang如何编写Markdown转HTML工具_golang Markdown转HTML工具编写详解
jvm·数据库·python
weixin_424999365 小时前
C#怎么使用TopLevel顶级语句 C#顶级语句怎么写如何省略Main方法简化控制台程序【语法】
jvm·数据库·python