类的加载原理(中)
2024-01-11 15:54:56
realizeClass
函数:类的加载和初始化
前言
类加载过程是 Java 虚拟机 (JVM) 的核心组件,负责加载、验证和初始化类。在之前的文章中,我们探讨了 _read_images
函数在类加载中的作用。虽然 _read_images
函数处理类的初始化,但它并不将其加载到 JVM 可用的内存区域中。这里就是 realizeClass
函数发挥作用的地方。
realizeClass
函数简介
realizeClass
函数旨在确保类被完全加载到 JVM 的只读 (ro
) 和读写 (rw
) 区域中,使其可以供 JVM 使用。当满足以下条件时,将调用 realizeClass
函数:
- 当类首次被引用时
- 当类通过反射调用时
- 当类加载器显式调用
realizeClass
方法时
realizeClass
函数工作流程
realizeClass
函数通过以下步骤实现其功能:
- 检查类的状态: 它检查类是否已经加载。如果不是,它将调用
linkClass
和prepareClass
方法。 - 分配内存: 它为类的实例分配内存。
- 初始化静态字段: 它初始化类的静态字段,包括常量和非静态最终字段。
- 调用构造函数: 它调用类的无参构造函数,完成类的初始化过程。
类的加载顺序
类的加载遵循以下顺序:
- 加载: JVM 查找并加载类文件。
- 链接: JVM 验证并链接类文件。
- 准备: JVM 分配内存并初始化静态字段。
- 解析: JVM 解析类中的方法和字段。
- 初始化: JVM 调用构造函数并完成类的初始化。
realizeClass
函数在类准备阶段之后调用,完成类初始化过程的最后一步。
性能影响
realizeClass
函数的调用会对性能产生一些影响,包括:
- 延迟加载: 类的加载被推迟到需要使用时,节省内存和启动时间。
- 并发加载: 多个线程可以同时调用
realizeClass
,导致类加载竞争。 - 死锁: 如果一个线程在调用
realizeClass
时被阻塞,而另一个线程也试图调用realizeClass
,则可能会发生死锁。
为了减轻这些影响,JVM 采用以下技术:
- 类加载锁: 每个类都有一个加载锁,防止多个线程同时调用
realizeClass
。 - 偏向锁: 如果一个线程频繁地调用某个类的
realizeClass
,JVM 将该类的加载锁偏向该线程。 - 锁消除: 如果类已经完全初始化,JVM 将消除其加载锁。
代码示例
以下代码示例展示了如何使用 realizeClass
函数:
public class MyClass {
private static int staticField;
public MyClass() {
System.out.println("MyClass constructor called");
}
public static void main(String[] args) {
// 显式调用 realizeClass 函数
MyClass.class.realizeClass();
// 创建 MyClass 实例
MyClass instance = new MyClass();
}
}
输出:
MyClass constructor called
总结
realizeClass
函数在类的加载和初始化过程中扮演着至关重要的角色。它确保类被完全加载到 JVM 的 ro
和 rw
区域中,使其可供 JVM 使用。虽然 realizeClass
函数会影响性能,但 JVM 采用了几种技术来减轻这些影响。
常见问题解答
-
realizeClass
函数是在类的加载期间还是初始化期间调用的?
realizeClass
函数在类的准备阶段之后和初始化阶段之前调用。 -
调用
realizeClass
函数有什么好处?
显式调用realizeClass
函数可以帮助 JVM 优化类加载过程,避免延迟加载和并发加载问题。 -
realizeClass
函数可以被哪个线程调用?
realizeClass
函数可以被任何线程调用,但最好在单线程上下文中调用它。 -
如何判断类是否已经完全初始化?
可以使用java.lang.Class.isFullyInitialized
方法来检查类是否已经完全初始化。 -
realizeClass
函数会创建类的实例吗?
不会,realizeClass
函数仅确保类被加载到 JVM 中,但不创建它的实例。