连载中 5/20

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

2026-09-13 · 158 阅读 · 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)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 1 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞