JVM 类加载:双亲委派机制

双亲委派机制:一句话说清楚它是啥

一句话总结

双亲委派就是------类加载时,先让父类加载器去加载,父类加载不了,才由自己加载

目的是保证核心类由高层加载器统一加载,防止被替换,确保安全和唯一。


一、什么是"双亲委派"?

"双亲"其实是翻译问题,它不是指"父母双方",而是"父辈"的意思。更准确的说法是"父类委派"。

它的规则只有一条:

当一个类加载器收到加载类的请求时,它不急着自己加载,而是先把请求交给父类加载器去处理。只有父类加载器明确表示"我加载不了",自己才动手。

这个"父",不是 Java 中的继承关系,而是类加载器之间的委托关系


二、类加载器有哪些?

Java 中的类加载器分为四层,从上到下是:

类加载器 实现方式 负责加载
Bootstrap ClassLoader JVM 用 C++ 实现 rt.jar 等核心类库(如 java.lang.*
Extension ClassLoader Java 实现 jre/lib/ext 目录下的扩展类
Application ClassLoader Java 实现 classpath 下的用户类(你的代码)
自定义 ClassLoader 继承 ClassLoader 特殊需求,如网络加载、热部署

它们之间形成一条链:

复制代码
自定义 → Application → Extension → Bootstrap

加载请求从下往上层层委派,一旦某一层能加载,就直接返回,不再往下。


三、为什么要有双亲委派?

1. 防止核心类被篡改

试想:如果你写了一个 java.lang.String,放在项目里。

如果没有双亲委派,Application ClassLoader 可能会加载你写的这个"假" String,那整个系统就乱了。

但因为有双亲委派,加载请求会先被委派到 Bootstrap ClassLoader,它从 rt.jar 中加载了真正的 String,你的类根本不会被加载。

✅ 所以,你永远无法替换核心类

2. 避免类重复加载

同一个类,比如 java.util.ArrayList,如果被多个类加载器各自加载,就会出现多个 Class 对象。

这会导致:

  • instanceof 判断失败;
  • 类型转换异常;
  • 静态变量不共享。

双亲委派确保这类核心类只被最顶层的加载器加载一次,保证唯一性。

3. 安全

核心类库由最可信的 Bootstrap ClassLoader 加载,它不走 Java 层代码,不受用户控制。

这种自顶向下的信任链,让 JVM 能抵御恶意代码的攻击。


四、双亲委派是怎么实现的?

核心就在 ClassLoader.loadClass() 方法里。

JDK 8 中的关键逻辑如下:

java 复制代码
protected Class<?> loadClass(String name, boolean resolve) {
    // 1. 先查自己是否已加载
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            // 2. 有父类,先让父类加载
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                // 父为 null,表示使用 Bootstrap
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 父类加载失败,继续
        }
        // 3. 父类没加载成功,自己加载
        if (c == null) {
            c = findClass(name);
        }
    }
    return c;
}

这个方法定义了标准流程

查缓存 → 委派父类 → 自己加载。

除非你重写它,否则所有类加载器都必须遵守。


五、双亲委派能打破吗?

能。通过重写 loadClass 方法,可以改成"先自己加载,再委派父类",这就是打破双亲委派

常见场景:

  • Tomcat:每个 Web 应用用独立类加载器,避免应用之间类冲突;
  • OSGi:模块化框架,支持不同模块用不同版本的同一个库;
  • 热部署:重新加载类而不重启 JVM。

代价:

  • 类唯一性没了;
  • 需要自己处理类隔离、冲突、卸载等问题;
  • 复杂度高,一般只在框架层面使用。

相关推荐
牛奔41 分钟前
Go 如何避免频繁抢占?
开发语言·后端·golang
寻星探路5 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
想用offer打牌6 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
曹牧7 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
KYGALYX7 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
爬山算法8 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
kfyty7258 小时前
集成 spring-ai 2.x 实践中遇到的一些问题及解决方案
java·人工智能·spring-ai
猫头虎8 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven