元空间耗尽直接锁定问题区域,表现为类或类加载器失控堆积;需通过日志、jstat、jcmd等工具确认动态趋势,并结合动态代理、类加载器泄漏等场景定位根因,调参须配合诊断修复。

看到 java.lang.OutOfMemoryError: Metaspace 就能直接锁定问题区域——不是堆、不是栈、不是直接内存,是元空间本身耗尽。它不反映对象数量多,而说明“类”或“类加载器”在失控堆积。
看日志,确认是不是元空间真出事了
别等服务挂了才行动。只要日志里出现这行,就是明确信号:
- java.lang.OutOfMemoryError: Metaspace
- GC 日志中反复出现 [Full GC (Metadata GC Threshold)] 或 [GC (Metadata GC Threshold)]
- 启动参数没设 -XX:MaxMetaspaceSize(
jinfo -flag MaxMetaspaceSize <pid></pid>返回-1),等于放任元空间无限吃本地内存
用 jstat 和 jcmd 实时盯住元空间行为
光看日志不够,要抓动态趋势:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
- 执行
jstat -gc <pid></pid>,重点关注 MU(已用)、MC(容量)、CCSU(压缩类空间已用)——如果 MU 持续上涨且逼近 MC,同时 CCSU 同步涨,基本就是类在疯长 - 执行
jcmd <pid> VM.classloader_stats</pid>,看 loadedClassCount 是否单向增加、unloadedClassCount 几乎不动——卸载失败,极大概率是类加载器泄漏 - 隔几秒连跑两次
jstat -class <pid></pid>,若 Loaded 明显增加而 Unloaded 几乎为 0,问题就坐实了
定位谁在疯狂造类或卡住类加载器
元空间膨胀只是表象,背后一定是代码或框架行为异常:
- 动态代理高发区:Spring Boot + CGLIB、MyBatis Mapper 接口、Groovy 脚本、JSP 编译、热部署(DevTools)、字节码增强(SkyWalking、Arthas 插件)
- 类加载器泄漏典型场景:Web 应用热部署后旧 ClassLoader 没被回收,它加载的所有类元数据全卡在元空间;常见于自定义 ClassLoader 持有 ThreadLocal、静态引用或未正确实现 findClass
- 快速筛查:加
-XX:+TraceClassLoading -XX:+TraceClassUnloading启动,重现场景后grep "Loaded" trace.log | wc -l统计加载量;或用jcmd <pid> VM.class_hierarchy</pid>看哪个 ClassLoader 加了上千个类
调参不是终点,得配合诊断和修复
只改 -XX:MaxMetaspaceSize 是治标。必须同步做这几件事:
- 压测定阈值:在准生产环境跑压测,用
jstat -gc <pid></pid>记录 MU 峰值,取其 1.5–2 倍设为-XX:MaxMetaspaceSize初始值 - 必设初始值:
-XX:MetaspaceSize=256m(例如),避免首次扩容就触发 Full GC - 启用类卸载:
-XX:+ClassUnloadingWithConcurrentMark(G1 默认开,CMS 需显式加) - 开启 NMT 查根因:
-XX:NativeMemoryTracking=summary启动后,用jcmd <pid> VM.native_memory summary</pid>看 Class 模块占用是否持续增长,再结合 classloader_stats 对照验证










