什么是双亲委派模型?
更新时间:2026-09-18。问题驱动短答页。双亲委派是 JVM 类加载的一条「向上请示」规则,理解它能解释很多
ClassNotFoundException/类冲突问题。
一句话回答
当一个类加载器要加载类时,它先把请求层层上抛给父加载器,只有父加载器在自己路径里找不到时,子加载器才自己加载。顺序是 Bootstrap → Extension/Platform → Application → 自定义。这样保证核心类(如 java.lang.Object)永远由最顶层加载、不会被假冒,且同一个类不会被重复加载。
加载层级

为什么要这样设计
- 安全:你自己写一个
java.lang.String放进 classpath,也不会被加载——请求先到 Bootstrap,加载的永远是 JDK 自带的核心类,防止核心 API 被篡改。 - 避免重复加载:父加载器已加载的类,子加载器直接用,不会各加载一份导致「同名类不相等」(
Class对象由「类加载器 + 全限定名」唯一确定)。
委派逻辑(ClassLoader.loadClass 的简化版):
java
protected Class<?> loadClass(String name, boolean resolve) {
Class<?> c = findLoadedClass(name); // 1. 是否已加载
if (c == null) {
try {
if (parent != null) c = parent.loadClass(name); // 2. 先委派父
else c = findBootstrapClass(name);
} catch (ClassNotFoundException e) { /* 父找不到 */ }
}
if (c == null) c = findClass(name); // 3. 父加载不到,自己加载
return c;
}什么时候会「打破」双亲委派
- SPI / JDBC:核心类(Bootstrap 加载的
DriverManager)要加载厂商在 classpath 里的实现类,但 Bootstrap 看不到用户 classpath。解决靠线程上下文类加载器(Thread Context ClassLoader)「逆向」去加载实现。 - OSGi / 热部署:需要模块间网状委派和类的卸载替换,自定义加载顺序。
- Tomcat:每个 Web 应用有独立 ClassLoader,优先加载自己目录里的类(WEB-INF/classes、lib),以实现不同应用间同名依赖的隔离——这是有意不遵守双亲委派(核心类如
java.*仍先委派,避免破坏安全)。
常见坑
- NoClassDefFoundError vs ClassNotFoundException:前者是编译期有、运行期找不到(或初始化失败),后者是加载时按名字找不到类,都和加载器的搜索路径有关。
- 同一个类被两个 ClassLoader 加载:在 JVM 看来是两个不同类型,强转/单例会失效(插件化、Tomcat 里最常见)。
- 以为放个同名
java.lang.String能替换 JDK 类:双亲委派会让你的类被忽略。
类加载在 JVM 内存里的归宿(方法区/Metaspace)见 JVM 运行时内存与堆分代;Metaspace 因动态类加载膨胀导致 Full GC 的排查见 Full GC 频繁怎么排查。
总结
双亲委派是类加载器收到请求后先委托父加载器、从 Bootstrap 到 Extension/Platform 再到 Application 层层向上、父加载不到才自己 findClass 的规则,价值是保证 java.* 等核心类只由 Bootstrap 加载不被篡改、并避免同一类被不同加载器重复加载(类由加载器+全限定名唯一标识);SPI/JDBC 通过线程上下文类加载器逆向加载实现类、Tomcat 让每个 Web 应用优先加载自己的类以隔离依赖,是典型的有意打破场景,排查 NoClassDef/类转换异常时要从「哪个加载器加载了类」入手。