JVM Metaspace 排障类加载太多也会把服务拖垮JVM 排障时大家经常关注堆内存和 GC却忽略 Metaspace。Metaspace 存放类元数据通常不像堆那样天天被盯着但一旦类加载失控服务会出现 Full GC、内存上涨、甚至OutOfMemoryError: Metaspace。这类问题在动态代理、热加载、脚本引擎、插件系统和频繁创建 ClassLoader 的场景里很常见。Metaspace 问题的关键不是简单把MaxMetaspaceSize调大而是找到类为什么越来越多。一、先确认是不是 Metaspace 压力flowchart TD A[Memory Rising] -- B{Heap Or Metaspace} B -- C[Heap Dump] B -- D[Class Loading Metrics] D -- E[ClassLoader Count] E -- F[Leak Candidate]先看 JVM 指标已加载类数量、卸载类数量、Metaspace used、committed、capacity。如果类数量只涨不降就要警惕。jcmd pid VM.native_memory summary jcmd pid GC.class_stats jstat -class pid 1000 5不同 JDK 对命令支持略有差异生产环境要提前验证工具链。Native Memory Tracking (NMT) 是排查 Metaspace 问题的重要工具。它可以帮助你了解 JVM native 内存的分配情况包括 Metaspace、CodeCache、Compiler、GC 等区域的使用。启用 NMT 会有一定性能开销建议在测试环境默认开启生产环境在排障时临时开启。通过对比不同时间点的 NMT 报告可以发现哪类元数据在持续增长。# 开启NMT -XX:NativeMemoryTrackingdetail # 查看基线 jcmd pid VM.native_memory baseline # 一段时间后对比 jcmd pid VM.native_memory summary.diff二、常见根因是 ClassLoader 泄漏类元数据通常跟 ClassLoader 生命周期绑定。如果 ClassLoader 被引用住里面加载的类就无法卸载。比如线程上下文 ClassLoader、静态集合、缓存、定时任务引用都可能导致泄漏。common_causes ├── repeated dynamic proxy generation ├── Groovy or JS script engine reload ├── plugin classloader not closed ├── thread context classloader retained └── static cache references generated classes框架越灵活越要关注类加载边界。动态能力不是免费的。排查 ClassLoader 泄漏时可以借助堆 dump 分析。用 MAT 或 JProfiler 查看 heap 中的 ClassLoader 实例重点关注实例数量异常多、保留大小大、或预期应该被回收却还存活的 ClassLoader。进一步可以查看这些 ClassLoader 被谁引用通常能在 GC Roots 路径中找到泄漏原因比如 static Map、线程 ThreadLocal、或长生命周期的对象引用。三、限制大小只是保护不是修复可以设置-XX:MaxMetaspaceSize防止进程无限吃内存但这只是边界保护。java \ -XX:MaxMetaspaceSize256m \ -XX:HeapDumpOnOutOfMemoryError \ -XX:NativeMemoryTrackingsummary \ -jar app.jar如果根因还在限制只会让服务更早失败。它的价值是把问题暴露出来并保护节点不被拖垮。四、线上排查要保留证据遇到 Metaspace 异常不要只重启。至少保留类加载统计、NMT 输出、线程 dump、关键日志和版本信息。evidence_pack ├── jstat class samples ├── jcmd VM.native_memory summary ├── thread dump ├── loaded class histogram ├── recent deployment version └── plugin or script reload records很多 Metaspace 问题和版本发布、配置热加载、插件升级有关。没有证据重启后只能等下一次复现。预防方面建议在代码评审时关注动态类加载的使用。每次引入动态代理、脚本引擎、插件系统或自定义 ClassLoader 时都要问清楚这些类什么时候卸载ClassLoader 什么时候回收有没有可能泄漏在测试环境可以编写长时间运行的稳定性测试观察类加载指标是否平稳。预防的成本远低于线上排障。在生产环境的监控侧建议将 Metaspace 使用率纳入告警体系。我们设置了两个告警级别Metaspace 使用率达到 80% 时发出 Warning 告警提前 24 小时通知达到 95% 时触发 Critical 告警需要立即介入排查。同时关注 ClassesLoaded 的持续增长趋势——如果 24 小时内加载了超过 10 万个新类且没有相应的卸载这通常是 ClassLoader 泄漏的信号。配合 Micrometer 的 JvmMemoryMetrics可以将这些指标无缝接入 Prometheus Grafana实现持续监控。建议在 Metaspace 告警的同时自动采集一份 heap dump 和 class histogram 快照避免事后排查时证据缺失。此外JDK 21 引入的-XX:UnlockDiagnosticVMOptions -XX:UnsyncloadClass可以帮助诊断并发类加载的阻塞问题。同时建议在测试环境运行长期的稳定性测试观察类加载指标曲线是否平稳。五、总结JVM Metaspace 问题通常来自类加载失控或 ClassLoader 泄漏。排查时先看类加载指标和 NMT再定位动态代理、脚本、插件、热加载等高风险场景。MaxMetaspaceSize是保护线不是修复方案。真正的解决办法是让不该长期存活的 ClassLoader 和动态类能被释放。