Java 类加载、类加载器及双亲委派机制详解
在Java中,类加载是JVM运行的重要环节,而类加载器则是负责将.class
文件加载到内存中的核心组件。本文详细介绍类加载的过程、类加载器的工作机制及双亲委派机制,同时比较Oracle JDK 8与JDK 9及之后版本在类加载器上的变化。
1. 类的加载
类的加载是什么?
类的加载是指将一个Java类的.class
文件读入内存,并转化为一个Class
对象。类的加载只会在类第一次被使用时触发,例如:
- 创建类的实例(
new
关键字)。 - 调用类的静态方法或访问静态字段。
- 使用类的子类(会触发父类加载)。
- 通过反射加载类。
- 使用
java
命令运行类。
类加载过程
类加载主要分为以下三个阶段:
- 加载:将
.class
文件的字节码读入内存,生成Class
对象。 - 链接:
- 验证:确保字节码文件格式合法。
- 准备:为类的静态变量分配内存并初始化为默认值。
- 解析:将符号引用解析为直接引用。
- 初始化:执行静态代码块和静态字段赋值。
代码示例:类加载过程
public class Demo {
static {
System.out.println("Class Demo is being initialized");
}
public static void main(String[] args) {
System.out.println("Main method executed");
}
}
运行时,类加载顺序如下:
- 加载
Demo
类的.class
文件。 - 链接:验证、准备静态变量。
- 初始化:执行静态代码块。
输出:
Class Demo is being initialized
Main method executed
2. 类加载器
类加载器是什么?
类加载器是JVM中负责加载字节码文件并生成Class
对象的组件。每个类加载器负责加载特定范围的类。
类加载器的分类
Java中的类加载器主要分为三种:
- 启动类加载器(Bootstrap ClassLoader):
- 负责加载JVM的核心类库,如
rt.jar
。 - 由本地代码实现,不是Java类对象。
- 负责加载JVM的核心类库,如
- 扩展类加载器(Extension ClassLoader):
- 加载
<JAVA_HOME>/lib/ext
目录下的扩展类库。
- 加载
- 应用程序类加载器(Application ClassLoader):
- 加载
CLASSPATH
中指定的类,是程序的默认类加载器。
- 加载
获取类加载器
代码示例:
public class ClassLoaderDemo {
public static void main(String[] args) {
// 获取当前类的类加载器
ClassLoader appClassLoader = ClassLoaderDemo.class.getClassLoader();
System.out.println("应用程序类加载器: " + appClassLoader);
// 获取父加载器(扩展类加载器)
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println("扩展类加载器: " + extClassLoader);
// 获取父加载器的父加载器(启动类加载器,返回 null)
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println("启动类加载器: " + bootstrapClassLoader);
}
}
运行结果(JDK 8):
应用程序类加载器: sun.misc.Launcher$AppClassLoader@2a139a55
扩展类加载器: sun.misc.Launcher$ExtClassLoader@7e774085
启动类加载器: null
3. 双亲委派机制
双亲委派机制是什么?
双亲委派机制是类加载器的一种设计模式,其核心思想是:
- 类加载器在加载类时,首先将加载请求委托给父加载器。
- 父加载器尝试加载,如果无法加载,则由当前加载器加载。
工作流程
假设需要加载类Demo
:
- 应用程序类加载器收到加载请求,先委托给扩展类加载器。
- 扩展类加载器再委托给启动类加载器。
- 启动类加载器尝试加载类。
- 如果启动类加载器无法加载,返回给扩展类加载器,最终由应用程序类加载器加载。
优点
- 安全性:避免核心类(如
java.lang.String
)被用户自定义类加载器篡改。 - 避免重复加载:同一个类只会被加载一次。
4. JDK 8 与 JDK 9 类加载器的变化
代码对比
JDK 8:
@Test
public void testClassLoader() {
ClassLoader classLoader = Demo.class.getClassLoader();
System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@22d8cfe0
System.out.println(classLoader.getParent()); // sun.misc.Launcher$ExtClassLoader@65e579dc
System.out.println(classLoader.getParent().getParent()); // null
}
JDK 9:
@Test
public void testClassLoader() {
ClassLoader classLoader = Demo.class.getClassLoader();
System.out.println(classLoader); // jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
System.out.println(classLoader.getParent()); // jdk.internal.loader.ClassLoaders$PlatformClassLoader@e73f9ac
System.out.println(classLoader.getParent().getParent()); // null
}
主要区别
- 扩展类加载器改为平台类加载器:
- JDK 8:扩展类加载器(
sun.misc.Launcher$ExtClassLoader
)。 - JDK 9:平台类加载器(
jdk.internal.loader.ClassLoaders$PlatformClassLoader
)。
- JDK 8:扩展类加载器(
- 模块化系统引入:
- JDK 9拆分了
rt.jar
,通过模块系统加载核心类。 - 平台类加载器专门用于加载模块化系统中的类。
- JDK 9拆分了
原因
- 模块化需求:JDK 9采用Java模块系统(JPMS),优化了类加载器设计。
- 安全性增强:移除了
sun.misc
包中的非标准实现。
总结
- 类的加载分为加载、链接、初始化三个阶段,类加载的触发条件包括首次使用类、反射等。
- 类加载器分为启动类加载器、扩展类加载器和应用程序类加载器,工作机制采用双亲委派模型。
- 双亲委派机制保证了类加载的安全性和高效性。
- JDK 9通过模块化改进了类加载器的设计,使用平台类加载器取代扩展类加载器,取消了双亲委派机制,提高了加载效率
通过理解类加载和类加载器的工作原理,可以更好地掌握Java应用程序的运行机制,确保开发的应用安全稳定。
标签:Java,JDK,System,器及,println,out,加载 From: https://www.cnblogs.com/itcq1024/p/18584040