类什么时候能被卸载?哪些常见写法会让整个类加载器(和它所有的类)永远留在内存里?这篇用 WeakReference 判断,逐种制造泄漏,再用 Metaspace 上限把后果跑出来。
[没有任何泄漏]
场景 clean → 加载器已被回收;已加载 771,累计卸载 1;Class 对象已回收
[静态字段里留着该类的一个实例]
场景 keepObject → 加载器仍然存活;已加载 772,累计卸载 0;Class 对象仍在
[静态字段里留着 Class 对象]
场景 keepClass → 加载器仍然存活;已加载 772,累计卸载 0;Class 对象仍在
[静态字段里留着类加载器]
场景 keepLoader → 加载器仍然存活;已加载 772,累计卸载 0;Class 对象仍在
[main 线程的 ThreadLocal 里留着实例]
场景 threadLocal → 加载器仍然存活;已加载 774,累计卸载 0;Class 对象仍在
[启动了一个线程,上下文类加载器设为它]
场景 thread → 加载器仍然存活;已加载 774,累计卸载 0;Class 对象仍在
[注册了一个持有实例的 shutdown hook]
场景 shutdownHook → 加载器仍然存活;已加载 780,累计卸载 0;Class 对象仍在
[线程池线程的 ThreadLocal 里留着实例]
场景 executor → 加载器仍然存活;已加载 819,累计卸载 0;Class 对象仍在
[把持有实例的 MBean 注册到 MBeanServer]
场景 mbean → 加载器仍然存活;已加载 1516,累计卸载 0;Class 对象仍在默认 日志里 unloading class 行数 1;示例 unloading class Payload 0x0000007001001000;加载器已被回收;已加载 771,累计卸载 1;Class 对象已回收 -XX:-ClassUnloading 日志里 unloading class 行数 0;示例 (无);加载器仍然存活;已加载 772,累计卸载 0;Class 对象仍在
JDK 21 UseSerialGC 加载器已被回收;已加载 771,累计卸载 1;Class 对象已回收 JDK 21 UseParallelGC 加载器已被回收;已加载 771,累计卸载 1;Class 对象已回收 JDK 21 UseG1GC 加载器已被回收;已加载 771,累计卸载 1;Class 对象已回收 JDK 21 UseZGC 加载器已被回收;已加载 800,累计卸载 1;Class 对象已回收 JDK 21 UseShenandoahGC 加载器已被回收;已加载 797,累计卸载 4;Class 对象已回收 JDK 27 UseSerialGC 加载器已被回收;已加载 807,累计卸载 1;Class 对象已回收 JDK 27 UseParallelGC 加载器已被回收;已加载 807,累计卸载 1;Class 对象已回收 JDK 27 UseG1GC 加载器已被回收;已加载 807,累计卸载 1;Class 对象已回收 JDK 27 UseZGC 加载器已被回收;已加载 807,累计卸载 1;Class 对象已回收 JDK 27 UseShenandoahGC 加载器已被回收;已加载 807,累计卸载 1;Class 对象已回收
不保留加载器(可卸载):
第 0 个加载器:Metaspace 已用 0 MB,累计卸载类 0
第 5000 个加载器:Metaspace 已用 3 MB,累计卸载类 3309
第 10000 个加载器:Metaspace 已用 3 MB,累计卸载类 8630
第 15000 个加载器:Metaspace 已用 2 MB,累计卸载类 13953
全部 20000 个完成;Metaspace 已用 2 MB,累计卸载类 19053
保留所有加载器:
第 0 个加载器:Metaspace 已用 0 MB,累计卸载类 0
在第 2556 个加载器时失败:java.lang.OutOfMemoryError: Metaspace
不保留加载器,但 -XX:-ClassUnloading:
第 0 个加载器:Metaspace 已用 0 MB,累计卸载类 0
在第 2550 个加载器时失败:java.lang.OutOfMemoryError: Metaspace