小米二面:JVM 触发类加载的条件有哪些?我说 new 的时候加载,然后他对我笑了笑......

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

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

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

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

类加载生命周期

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

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

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

主动引用

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

1. 创建类的实例

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

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

初始化流程:

  • 分配内存给 MyClass 的实例对象。
  • 加载 MyClass 类的字节码,并执行静态代码块和静态变量赋值操作。

2. 访问类的静态字段或静态方法

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

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

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

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

3. 反射

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

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

4. 初始化类的子类时,先初始化父类

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

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

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

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

5. 拟机启动时,初始化 main 方法所在的类

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

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

6. 动态语言支持

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

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

被动引用:不触发类加载

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

1. 通过子类引用父类的静态字段

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

示例

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

2. 访问编译期常量

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

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

3. 通过数组定义类引用

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

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

码哥,为什么需要关注类加载的时机?

  • 避免类的过早加载:过早加载可能导致不必要的内存消耗,尤其在大型应用中。
  • 延迟加载(Lazy Loading):通过延迟加载,可以在真正需要时才加载类,减少启动时间。
  • 减少类加载冲突:在模块化或插件化的应用中,合理安排类加载顺序有助于避免类冲突和类加载死锁问题。
责任编辑:姜华 来源: 码哥跳动
相关推荐

2024-03-26 07:58:12

Redis编程模型

2021-05-19 08:31:15

压测数据结构与算法工具

2020-07-23 07:26:49

JVM类加载器

2024-07-08 10:11:37

2024-03-08 08:26:25

类的加载Class文件Java

2024-12-04 09:01:55

引导类加载器C++

2021-05-08 09:02:19

Java加载器

2012-03-13 14:41:41

JavaJVM

2024-03-12 07:44:53

JVM双亲委托机制类加载器

2023-10-31 16:00:51

类加载机制Java

2017-09-20 08:07:32

java加载机制

2021-04-29 11:18:14

JVM加载机制

2017-03-08 10:30:43

JVMJava加载机制

2024-12-02 09:01:23

Java虚拟机内存

2024-04-22 00:00:00

CASCPU硬件

2017-09-22 15:25:40

Go语言其他语言错误处理

2015-07-03 10:12:41

小米迅雷

2010-03-08 08:39:54

类加载器newJava

2020-02-11 13:22:20

this函数JavaScript

2011-11-24 10:00:10

Xtend
点赞
收藏

51CTO技术栈公众号