Java 中常见的类加载器(Bootstrap、Extension、Application/System ClassLoader)及其负责加载的路径
在 Java 中,类加载器(ClassLoader)负责将编译好的 .class 字节码文件加载到 JVM 内存中。Java 默认提供了三个核心的类加载器,它们形成了一个层级结构。
以下是这三种常见类加载器的详细介绍及其负责加载的路径(注:以下路径描述主要基于 Java 8 及更早版本,Java 9 引入模块化后有一定改变,文末会有补充说明)。
1. Bootstrap ClassLoader(启动类加载器)
这是最顶层的类加载器,它是 JVM 自身的一部分。
- 实现语言:C/C++(在 HotSpot 虚拟机中)。由于它不是 Java 编写的,所以在 Java 代码中获取它会返回
null(例如String.class.getClassLoader() == null)。 - 职责:负责加载 Java 的核心类库(即 Java 运行最基础的类,如
java.lang.*、java.util.*等)。 - 加载路径:
<JAVA_HOME>/jre/lib目录下的核心 jar 包,最典型的是rt.jar、resources.jar、charsets.jar等。
- 对应的系统属性:可通过
System.getProperty("sun.boot.class.path")查看其加载的具体路径。
2. Extension ClassLoader(扩展类加载器)
它是启动类加载器的下级(在逻辑上,而非继承关系)。
- 实现语言:Java(具体实现类为
sun.misc.Launcher$ExtClassLoader)。 - 职责:负责加载 Java 的扩展类库。Sun/Oracle 预留了这个目录,允许开发者将一些需要全局使用的第三方或扩展 jar 包放在这里,JVM 会自动加载。
- 加载路径:
<JAVA_HOME>/jre/lib/ext目录。- 或者由系统变量
-Djava.ext.dirs指定的目录中的类库。
- 对应的系统属性:可通过
System.getProperty("java.ext.dirs")查看。
3. Application / System ClassLoader(应用程序/系统类加载器)
它是扩展类加载器的下级,也是普通 Java 程序的默认类加载器。
- 实现语言:Java(具体实现类为
sun.misc.Launcher$AppClassLoader)。 - 职责:负责加载开发者自己编写的类以及第三方依赖(Maven/Gradle引入的包)。
- 加载路径:
- 环境变量
CLASSPATH指定的目录。 - 命令行启动时通过
-cp或-classpath指定的目录和 jar 包。
- 环境变量
- 获取方式:可以通过
ClassLoader.getSystemClassLoader()方法直接获取。 - 对应的系统属性:可通过
System.getProperty("java.class.path")查看。
💡 核心机制:双亲委派模型 (Parent Delegation Model)
这三个类加载器协同工作的方式被称为双亲委派模型。它的工作流程如下:
- 当 AppClassLoader 收到一个加载类的请求时,它首先不会自己去加载,而是把请求委派给父加载器(ExtClassLoader)。
- ExtClassLoader 收到请求后,同样不会自己先加载,而是继续委派给它的父加载器(Bootstrap ClassLoader)。
- Bootstrap ClassLoader 尝试在其负责的路径(
jre/lib)寻找。如果找到,就加载并返回;如果找不到,就将请求退回给子加载器。 - ExtClassLoader 尝试在
jre/lib/ext寻找。找到就加载,找不到就退回给 AppClassLoader。 - AppClassLoader 尝试在
CLASSPATH寻找。如果依然找不到,就会抛出ClassNotFoundException。
为什么这样做?(核心优点)
- 沙箱安全机制:防止核心 API 被篡改。例如,如果你自己写了一个
java.lang.String类,由于双亲委派,请求会一直送到顶层,Bootstrap 会加载官方核心库中的String类,而你自己写的String类永远不会被加载。 - 避免类的重复加载:父类加载器已经加载过的类,子加载器不需要再加载一次。
⚠️ 补充:Java 9 及之后的改变 (模块化系统 JPMS)
从 Java 9 开始,引入了模块化系统,类加载架构发生了一些调整:
- 没有了
rt.jar和ext目录:JDK 核心类库被拆分成了多个 JMOD 文件(放在<JAVA_HOME>/jmods目录下)。 - Extension ClassLoader 被重命名为 Platform ClassLoader(平台类加载器):
- 因为
ext目录被取消了,扩展机制被移除。 - Platform ClassLoader 负责加载 Java SE 和 JDK 的部分模块。
- 因为
- Bootstrap ClassLoader 依然存在,但现在它在 JVM 内部负责加载基础的核心模块(如
java.base、java.logging等)。 - 双亲委派机制依然保留,但增加了一个模块寻址的步骤:在委派给父加载器之前,会先判断该类属于哪个具体的模块,如果属于某个已知的模块,就会直接把请求交给负责该模块的类加载器。
验证代码(在 Java 8 下运行)
你可以运行以下代码直观地感受它们:
java
public class ClassLoaderTest {
public static void main(String[] args) {
// 1. 获取自定义类的加载器 (AppClassLoader)
ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println("当前类的加载器: " + appClassLoader);
// 2. 获取 AppClassLoader 的父加载器 (ExtClassLoader)
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println("父加载器: " + extClassLoader);
// 3. 获取 ExtClassLoader 的父加载器 (Bootstrap ClassLoader)
ClassLoader bootClassLoader = extClassLoader.getParent();
System.out.println("祖父加载器: " + bootClassLoader); // 输出 null,因为是 C++ 实现的
// 4. 获取核心类 String 的加载器
ClassLoader stringClassLoader = String.class.getClassLoader();
System.out.println("String类的加载器: " + stringClassLoader); // 输出 null,由 Bootstrap 加载
}
}