双亲委派模型
外观
概述[编辑 | 编辑源代码]
双亲委派模型是Java虚拟机(JVM)在类加载阶段采用的一种层次化责任分配机制。其核心思想是:当一个类加载器收到类加载请求时,首先将请求委派给父类加载器处理,仅当父类加载器无法完成加载时,子类加载器才会尝试自行加载。这种模型通过层级关系避免了类的重复加载,并保障了Java核心库的安全性。
核心原理[编辑 | 编辑源代码]
双亲委派模型通过以下三个核心类加载器实现层级关系:
- 启动类加载器(Bootstrap ClassLoader):加载JRE核心库(如
rt.jar
),由C++实现,无Java对应类。 - 扩展类加载器(Extension ClassLoader):加载
jre/lib/ext
目录下的扩展类。 - 应用程序类加载器(Application ClassLoader):加载用户类路径(ClassPath)下的类。
层级关系图[编辑 | 编辑源代码]
工作流程[编辑 | 编辑源代码]
双亲委派模型的类加载流程如下:
- 子类加载器收到类加载请求。
- 检查是否已加载该类,若已加载则直接返回。
- 若未加载,委派请求给父类加载器。
- 父类加载器递归执行相同逻辑,直到启动类加载器。
- 若父类加载器无法完成加载,子类加载器调用
findClass()
方法尝试加载。
伪代码实现[编辑 | 编辑源代码]
protected Class<?> loadClass(String name, boolean resolve) {
synchronized (getClassLoadingLock(name)) {
// 1. 检查是否已加载
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 委派给父类加载器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {}
// 3. 父类无法加载时自行加载
if (c == null) {
c = findClass(name);
}
}
return c;
}
}
实际案例[编辑 | 编辑源代码]
案例1:避免核心类被篡改[编辑 | 编辑源代码]
若用户自定义一个java.lang.String
类并尝试加载,由于双亲委派机制会优先委派给启动类加载器加载核心库中的String
,自定义类将不会被加载,从而保证核心库的安全性。
案例2:Tomcat的类加载优化[编辑 | 编辑源代码]
Tomcat为每个Web应用提供独立的WebappClassLoader
,但打破了双亲委派模型:
- 先尝试自行加载(如Web应用内的类)。
- 失败后再委派给父类加载器。
- 实现了应用间类的隔离。
打破双亲委派的情况[编辑 | 编辑源代码]
以下场景需打破双亲委派模型:
- SPI机制(如JDBC驱动加载):通过线程上下文类加载器(
Thread.currentThread().getContextClassLoader()
)实现。 - 热部署:如OSGi框架通过网状类加载器结构实现模块化。
数学表示[编辑 | 编辑源代码]
类加载过程可形式化为:
常见问题[编辑 | 编辑源代码]
总结[编辑 | 编辑源代码]
双亲委派模型通过层级委派机制实现了类的唯一性保护和核心库安全隔离,是JVM类加载体系的基石。理解其原理有助于解决类冲突、实现热部署等高级特性。