要实时追踪“可达性分析判定 → 类卸载”全过程,必须组合启用-XX:+TraceClassUnloading、-XX:+PrintGCDetails和-XX:+PrintClassLoaderStatistics三项参数,并主动断链实例、ClassLoader及Class引用,再触发Full GC观察Metaspace下降、卸载日志与统计数增加。

要实时追踪“可达性分析判定 → 类卸载”全过程,关键不是单独开启某个参数,而是组合启用日志、触发回收条件,并结合 GC 行为观察类是否真正被卸载。整个过程依赖 JVM 的可达性分析机制,而跟踪参数只是把幕后动作“显性化”。
启用核心跟踪参数
必须同时开启以下三项,才能完整看到从判定到卸载的链条:
- -XX:+TraceClassUnloading:输出每一轮 GC 中被卸载的类名(如 [Unloading class com.example.DynamicService 0x0000000800a2c000])
- -XX:+PrintGCDetails:显示每次 GC 类型(Young GC / Full GC)、各代内存变化,以及 Metaspace 区域使用量变化(如 [Metaspace: 12456K->12300K(1056768K)]),这是判断卸载是否生效的直接依据
- -XX:+PrintClassLoaderStatistics:统计类加载器数量及已卸载数(如 Classes: total=12456, unloaded=32),确认 ClassLoader 是否被回收
主动构造并验证可达性断链
仅开参数不会自动卸载——必须让 JVM 在 GC 时判定“该类不可达”。需人为满足三重断链:
- 清除所有该类的实例(instance = null,并确保无强引用路径)
- 显式置空自定义 ClassLoader 引用(loader = null),避免被线程栈或静态变量持有
- 解除对 Class 对象的引用(如 clazz = null;避免 Class.forName(...) 后长期持有返回值)
之后调用 System.gc() 或等待 Metaspace 达阈值触发 Full GC(推荐用 -XX:MaxMetaspaceSize=64m 加速触发)。
从日志中识别全过程信号
一次成功的卸载在日志中呈现为连贯信号链:
- 先出现 Full GC 日志,且 Metaspace 使用量下降(如 12456K→12300K)
- 紧随其后出现 [Unloading class ...] 行,对应被回收的类名
- 同一轮 GC 日志末尾出现 ClassLoaderStatistics,显示 unloaded 数量增加
- 若某类反复加载但 unloaded 数不增、Metaspace 持续上涨,说明仍有引用未断——此时需用 jstack 或 jmap -histo 追查残留引用
注意回收器兼容性
不同垃圾收集器对类卸载支持不同:
- G1(JDK9+ 默认):自动启用类卸载,无需额外参数,但需确保 -XX:+UseG1GC
- CMS(已废弃):必须配 -XX:+CMSClassUnloadingEnabled
- ZGC / Shenandoah:默认支持,但需 JDK11+,且日志中仍需 -XX:+TraceClassUnloading 才可见卸载记录

















