当前位置: 首页 > news >正文

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

目录

一、类加载生命周期

二、主动引用

2.1、创建类的实例

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

2.3、反射

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

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

2.6、动态语言支持

三、被动引用

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

3.2、访问编译期常量

3.3、通过数组定义类引用


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

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

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

一、类加载生命周期

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

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

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

二、主动引用

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

2.1、创建类的实例

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

// 触发 MyClass 的加载和初始化
MyClass obj = new MyClass(); 

初始化流程

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

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

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

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

// 触发 MyClass 的加载
System.out.println(MyClass.staticVar);  
// 触发 MyClass 的加载
MyClass.staticMethod();                

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

System.out.println(MyClass.FINAL_CONSTANT);  // 不触发类加载

2.3、反射

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

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

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

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

public class Parent {static {System.out.println("父类初始化");}
}public class Child extends Parent {static {System.out.println("子类初始化");}
}// 先输出"父类初始化",再输出"子类初始化"
Child obj = new Child();  

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

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

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

2.6、动态语言支持

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

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

三、被动引用

被动引用不触发类加载。

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

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

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

示例

// 只触发 Parent 的加载,不触发 Child 的加载
System.out.println(Child.staticVar);  

3.2、访问编译期常量

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

// 不触发 MyClass 的加载
System.out.println(MyClass.FINAL_CONSTANT);  

3.3、通过数组定义类引用

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

// 不触发 MyClass 的加载
MyClass[] array = new MyClass[10];  

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

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

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

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

http://www.lryc.cn/news/520719.html

相关文章:

  • Android实战经验篇-增加系统分区
  • 深入学习 Python 量化编程
  • 机器学习笔记——特征工程
  • 4种革新性AI Agent工作流设计模式全解析
  • 【入门级】计算机网络学习
  • 安装 Jenkins 后无法访问用户名或密码且忘记这些凭证怎么办?
  • day08_Kafka
  • 安装conda 环境
  • 【dockerros2】ROS2节点通信:docker容器之间/docker容器与宿主机之间
  • 使用外网访问在群晖中搭建思源docker
  • 深度学习中的EMA技术:原理、实现与实验分析
  • win32汇编环境,窗口程序中对按钮控件常用操作的示例
  • CentOS 7.9 通过 yum 安装 Docker
  • 【开源免费】基于Vue和SpringBoot的英语知识应用网站(附论文)
  • 工具推荐:PDFgear——免费且强大的PDF编辑工具 v2.1.12
  • Web渗透测试之XSS跨站脚本 防御[WAF]绕过手法
  • MMDetection框架下的常见目标检测与分割模型综述与实践指南
  • 怎么实现Redis的高可用?
  • OpenCV实现Kuwahara滤波
  • WINFORM - DevExpress -> DevExpress总结[安装、案例]
  • Golang学习笔记_22——Reader示例
  • 【2024年华为OD机试】(A卷,100分)- 猜字谜(Java JS PythonC/C++)
  • iostat命令详解
  • Linux:操作系统简介
  • 企业级信息系统开发讲课笔记4.12 Spring Boot默认缓存管理
  • 2025制定一个高级java开发路线:分布式系统、多线程编程、高并发经验
  • 20250110_ PyTorch中的张量操作
  • hadoop-yarn常用命令
  • LabVIEW滤波器功能
  • 【Unity3D日常开发】Unity3D中打开Window文件对话框打开文件(PC版)