元空间oom主因是高频动态生成匿名类且未卸载,需通过jstat/jcmd/arthas定位生成源,统一复用序列化配置、禁用非必要动态代理,并设置合理metaspace参数。

高频动态生成匿名类(如 FastJSON 的 ASMSerializer、Jackson 的 BeanDeserializer、CGLIB 代理类等)会持续向元空间加载类元数据,若未被及时卸载,就会造成元空间持续增长、GC 失效、最终触发 OutOfMemoryError: Metaspace。排查关键不是看“序列化用了什么”,而是确认「谁在反复生成新类」「类为何不卸载」。
确认元空间恶化是否真由序列化动态类引起
先排除混淆因素(如反射膨胀、Groovy 脚本、热部署残留):
- 运行
jstat -gc <pid></pid>,重点观察 MU(Metaspace used)是否线性上涨、MC(Metaspace capacity)是否逼近MX、且 MGCC(Metaspace GC 次数)长期为 0 或极低——说明元空间几乎没被回收 - 执行
jcmd <pid> VM.native_memory summary scale=MB</pid>,再追加detail,检查 class 类型占用是否超 60%,并留意是否有大量ASMSerializer_、ASMDeserializer_、$$EnhancerBySpringCGLIB$$等命名模式 - 临时启用
-XX:+TraceClassLoading -XX:+TraceClassUnloading(仅限测试环境),日志中若高频出现带数字后缀的匿名类(如ASMSerializer_UserDTO_123、BeanDeserializerForInt_456),基本可锁定 - 用
jmap -clstats <pid></pid>查类加载器统计:若发现大量sun.misc.Launcher$AppClassLoader或自定义类加载器下挂载了成百上千个 ASM/Proxy 类,说明缓存失控或类加载器泄漏
定位具体是哪个序列化工具或调用路径在生成类
动态类生成有明确触发条件(首次使用某类型时生成),因此要抓「高频新建配置实例」或「非复用式调用」:
-
FastJSON:检查是否每个请求都 new
SerializeConfig或ParserConfig;全局应只用SerializeConfig.globalInstance(),禁用局部实例 -
Jackson:确认
ObjectMapper是否单例复用;避免每次反序列化都 newObjectReader或频繁调用configure(DeserializationFeature, true)(会触发新 deserializer 生成) -
Spring AOP/CGLIB:检查是否对工具类、第三方包、甚至
final类开启代理;用@EnableAspectJAutoProxy(proxyTargetClass = false)强制走 JDK 接口代理(只生成一个$ProxyN) - 用
Arthas watch监控关键方法,例如:watch com.alibaba.fastjson.serializer.SerializeConfig addSerializer '{params,returnObj}' -n 5watch org.springframework.cglib.proxy.Enhancer create '{params,returnObj}' -n 5
验证与缓解:从机制上阻断重复生成
不能只靠加大 -XX:MaxMetaspaceSize,重点是让类可复用、少生成、能卸载:
-
强制复用已有动态类:FastJSON 设置
SerializeConfig.globalInstance().setAsmEnable(true)(默认开启),并确保config.setAsmEnable(false)不被误设;Jackson 启用DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY等稳定特性,减少分支生成 -
切断类加载器强引用链:若使用自定义类加载器加载序列化器,确保其无静态引用、未注册到 ThreadLocal、监听器已反注册;检查是否有
static Map<class serializer></class>缓存了 Class 对象 -
降级或替换方案:对简单 DTO,改用
StringRedisTemplate+ JSON 字符串;对高性能场景,迁移到 Protobuf(需预编译 schema,零运行时类生成) -
参数辅助兜底:设置
-XX:MetaspaceSize=256m(避免早期小容量频繁 GC)、-XX:MaxMetaspaceSize=512m(防失控增长)、-XX:MinMetaspaceFreeRatio=40 -XX:MaxMetaspaceFreeRatio=80(减少碎片)
问题本质是「类生成不可控」+「卸载条件不满足」,修复核心在于统一配置实例、复用 ObjectMapper/SerializeConfig、收敛代理范围,并通过工具链快速定位生成源头。不复杂但容易忽略。











