插件服务每天加载五千个脚本类,Metaspace 从 200MB 涨到 1.4G,jcmd GC.class_stats 里已加载类 6.8 万个,已卸载 0 个。碰到这种情况,我一般先确认改动有没有真的生效。

原因不复杂,类要被卸载,需要加载它的 ClassLoader 不可达、类实例全部回收、Class 对象没有被引用。插件卸载后静态注册表还留着 Class 对象,一条都不满足。

碰到「类卸载条件」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。

java -Xlog:class+load=info,class+unload=info -jar plugin-host.jar
jcmd 21455 GC.class_stats | awk '{print $2}' | tail -5
grep -c 'class,unload' /var/log/app/class.log

权限不要一口气开到最大,先加到够用再往上试,范围小才好收。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。

具体做法是,确认每次插件更新是否新建了 ClassLoader;卸载时清空静态注册表里的 Class 和实例引用;停掉插件创建的线程 线程的 contextClassLoader 会拉住加载器;用 -Xlog:class+unload=info 确认卸载真的发生。

最后用这个标准收尾,插件卸载后日志里出现 class unload 记录 Metaspace 回落到 300MB 附近。

作者 9823189