连载中 5/20

双亲委派:为什么类加载器要讲究先来后到

2026-09-13 · 154 阅读 · 0 评论 · 0 赞

一个反直觉的事实

JVM 里判断两个类是否相同,除了看 Class 文件,还要看加载它的类加载器是否相同。同一个 User.class,被加载器 A 和加载器 B 各加载一次,堆里就有两个互不相干的 Class 对象——instanceof 返回 false,互相赋值直接 ClassCastException。第一次听说的人多半以为是 bug,其实这是整个类加载体系的基石设计:加载器就是类的命名空间。

三层加载器

启动类加载器(Bootstrap,C++ 实现)负责 Java 核心库,在 JDK 9 后对应平台模块;平台类加载器(JDK 8 里叫扩展类加载器)负责扩展类;应用类加载器负责 classpath 上你自己写的类。三层各管一段、逐级向上委派,程序里默认的加载器就是应用类加载器。

工作模型:先问爹,再自己干

// ClassLoader.loadClass 的核心逻辑(简化)
protected Class<?> loadClass(String name, boolean resolve) {
    // 1. 先查自己有没有加载过(缓存)
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        // 2. 有爹先问爹:父加载器能加载就交给父加载器
        if (parent != null) {
            c = parent.loadClass(name, false);
        } else {
            c = findBootstrapClassOrNull(name);
        }
        // 3. 爹加载不了,自己才动手(findClass)
        if (c == null) {
            c = findClass(name);
        }
    }
    return c;
}

这个模型买来两样东西:安全——自己写一个 java.lang.String 塞进 classpath 也不会生效,因为委派到最顶层时核心库早已加载,你的假货永远排不上号;唯一——同一个类全局只有一份,避免类型体系混乱。

打破规则的场景

双亲委派不是法律,是建议。三类场景会光明正大地打破它:SPI 与 JDBC——核心库里的 DriverManager 要加载 classpath 上的厂商驱动(父加载器"够不着"子加载器的类),解决办法是线程上下文类加载器,让父加载器反过来"借用"子加载器的手;Tomcat 的应用隔离——每个 webapp 一个独立加载器,先自己加载 webapp 内的类,加载不到才委派,两个应用里同名不同版的类互不干扰,卸载应用时整个加载器连同类一起回收;热部署与模块化——OSGi、代码热更新都靠"换加载器"实现类的替换与卸载。

自己写一个加载器

public class ReloadableLoader extends ClassLoader {
    private final String dir;

    public ReloadableLoader(String dir) { this.dir = dir; }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes = readClassFile(dir, name);   // 自己定义字节流来源
        if (bytes == null) throw new ClassNotFoundException(name);
        return defineClass(name, bytes, 0, bytes.length);
    }
}
// 注意:委派逻辑在 loadClass 里,继承时重写的是 findClass
// 只有需要破坏双亲委派(如 Tomcat)才重写 loadClass

记住这个分工:重写 findClass 是遵守规则的扩展,重写 loadClass 是打破规则的革命。类加载的故事到这就齐了,下一篇进入系列的第二大段——垃圾回收的第一课:JVM 怎么判断一个对象是不是垃圾。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#双亲委派#类加载器#Tomcat隔离#SPI#自定义类加载器

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞