元空间溢出本质是类元数据占用本地内存失控,需设硬上限(如-xx:maxmetaspacesize=512m)、控回收节奏(如-xx:metaspacesize=384m)、查真实来源(如cglib代理、热部署泄漏)。

元空间溢出(java.lang.OutOfMemoryError: Metaspace)本质是类元数据占用本地内存失控,不是堆不够,而是“类太多、卸不掉、收不住”。解决核心是:设硬上限、控回收节奏、查真实来源。
必须设置两个关键上限参数
Java 8+ 默认不限制元空间大小,它会一直向操作系统申请内存,直到拖垮整个进程或被系统 OOM Killer 杀掉。因此第一步就是强制划边界:
- -XX:MaxMetaspaceSize=512m:硬性上限,达到即抛 OOM;中小 Spring Boot 应用推荐 300m–600m,含大量 CGLIB/AOP/字节码生成的可设 768m–1g
- -XX:MetaspaceSize=384m:触发首次元空间 GC 的阈值(不是初始分配量),建议设为 Max 的 1/2 到 2/3,避免启动期频繁 GC 或延迟清理
用比例参数稳住 GC 行为
只设上下限容易导致元空间容量反复伸缩——GC 后空闲太多就收缩,刚收缩完又扩容,引发抖动。通过两个比例参数让 GC 更“理性”:
- -XX:MinMetaspaceFreeRatio=40:GC 后若空闲空间占比低于 40%,下次 GC 前尝试扩容
- -XX:MaxMetaspaceFreeRatio=70:GC 后若空闲空间占比高于 70%,可能主动收缩释放本地内存
这两个值默认就是 40 和 70,多数场景无需改;若观察到 MU/MC 在 30%–90% 间剧烈震荡,再微调。
上线后必须验证是否生效
加了参数≠起效,得看真实行为:
- 启动时加
-Xlog:gc*:file=gc.log,日志中搜索Metaspace,确认有used、capacity、committed输出 - 运行中执行
jstat -gc <pid></pid>,重点关注MU(已用)和MC(当前容量),看是否稳定在设定范围内,且MU/MC长期不超 90% - 压测时模拟高频类加载(如动态脚本、反复 AOP 代理),确认不再 OOM,而是平稳触发元空间 GC
别只调参,要揪出类增长根源
参数是兜底手段,真正吃掉元空间的是代码和依赖:
- 检查是否滥用
CGLIB、Byte Buddy、Groovy等字节码生成框架;CGLIBsetUseCache(false)这类写法极易导致类爆炸 - 排查 Spring AOP、自定义注解处理器、热部署框架(如 DevTools)、监控埋点 SDK(尤其带独立 ClassLoader 的)
- 确认没有重复打包(如多个
spring-boot-starter冲突)、同一 jar 被多个 ClassLoader 加载 - 怀疑泄漏时,用
jcmd <pid> VM.native_memory summary scale=MB</pid>查 Metaspace 是否持续上涨,再用jmap -clstats <pid></pid>看活跃类加载器数量是否异常增加
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











