FastJson内存溢出既可能发生在堆内(如Java heap space),也可能发生在堆外(如Metaspace、直接内存),主因是缓存机制滥用、泛型动态生成、全量解析大JSON及配置不当;需分别通过流式解析、TypeReference复用、缓存清理和版本升级应对。

FastJson 引起的内存溢出,既可能发生在堆内(java.lang.OutOfMemoryError: Java heap space),也可能发生在堆外(如直接内存、元空间、CodeCache 或 native 内存),两者成因和表现不同,但都与 FastJson 的缓存机制、对象创建模式、泛型使用方式及配置策略密切相关。
堆内存溢出:大对象、深结构、未分页解析
典型场景是序列化或反序列化超大 JSON(如含千万级元素的 JSONArray)、嵌套极深的对象树,或存在巨型字符串字段(如 base64 图片、长文本日志)。
-
根本原因:FastJson 默认将整个 JSON 加载为内存对象(如
JSONArray、JSONObject),其底层SerializeWriter.buf是char[],一旦遇到单个超长字段(如cost字段含 1.8 亿元素),缓冲区会申请数百 MB 连续内存,直接撑爆堆。 -
关键证据:堆 dump 中
[C(char[])和[Ljava.lang.Object;排名靠前;SerializeWriter.expandCapacity抛出 OOM;jstat -gcutil显示老年代持续满且 GC 无效。 -
应对措施:
- 避免全量加载:改用
JSONReader流式解析,逐条读取数组项,不保留全部在内存 - 限制输入规模:服务端对 JSON 大小做前置校验(如
Content-Length或解析前截断) - 拆分大数据:将超大数组按页/批次处理,例如只序列化 ID 列表,详情异步拉取
- 避免全量加载:改用
堆外内存溢出:ParserConfig 缓存、ParameterizedTypeImpl 泄漏
这不是 JVM 堆的问题,而是 DirectByteBuffer、Metaspace 或 native 内存被持续占用,表现为进程 RSS 持续上涨(远超 -Xmx 设置),jmap -heap 看堆正常,但 pmap -x 或 NMT 显示 native 内存飙升。
-
根本原因:
-
ParserConfig.getGlobalInstance().deserializers使用IdentityHashMap缓存反序列化器,key 是每次 new 出的ParameterizedTypeImpl实例 —— 即使类型语义相同,因 == 比较失败,缓存不断膨胀 - 大量动态类(如运行时生成的代理类、匿名内部类)被 FastJson 的
JavaBeanDeserializer构建并缓存,占用 Metaspace - 自定义
ObjectSerializer/ObjectDeserializer中持有外部引用或未关闭资源,导致关联 native 对象无法释放
-
-
关键证据:MAT 分析显示
FieldInfo、JavaBeanDeserializer实例数异常高;jcmd <pid> VM.native_memory summary显示Internal或Other区域持续增长;lsof -p <pid> | grep anon_inode发现大量未关闭的 direct buffer -
应对措施:
- 泛型反序列化统一复用
TypeReference或静态ParameterizedTypeImpl,禁止每次 new - 定期清理全局缓存:
ParserConfig.getGlobalInstance().clearDeserializers()(注意线程安全) - 禁用不必要的特性:如非必要,关闭
Feature.SupportArrayToBean、Feature.DisableCircularReferenceDetect(它本身也占内存)
- 泛型反序列化统一复用
配置与版本风险:被忽略的默认行为
FastJson 许多“性能优化”默认开启,但在高并发、多类型、长周期服务中反而成为泄漏温床。
立即学习“Java免费学习笔记(深入)”;
-
常见高危配置:
-
SerializerFeature.WriteClassName:强制写入类名,增大输出体积,加剧缓冲区压力 -
SerializerFeature.PrettyFormat:格式化会显著放大char[]容量需求 - 未设
ParserConfig.getGlobalInstance().setMaxCacheSize(1024),缓存无上限
-
-
版本建议:务必升级至
fastjson2(com.alibaba.fastjson2)或至少fastjson 1.2.83+。旧版(如 1.2.24–1.2.68)存在多个已知IdentityHashMap和SerializeWriter泄漏缺陷,官方 commit 已修复(如ef50a5b)。 -
替代方案:对稳定性要求极高的系统,可评估迁移至
jackson-databind(配合JsonParser流式 API)或gson(更轻量、无全局静态缓存)。
快速定位三板斧
无需等待 OOM,日常监控即可提前发现苗头:
-
堆内:用
jstat -gc <pid> 5000观察OU(老年代使用率)是否缓慢爬升且 FullGC 后不下降;jmap -histo <pid> | head -20关注[C、JSONArray、JSONObject实例数 -
堆外:启动时加
-XX:NativeMemoryTracking=detail,用jcmd <pid> VM.native_memory summary scale=MB对比各时段差异;pmap -x <pid> | tail -20查看最大几块内存映射 -
代码层:全局搜索
new ParameterizedTypeImpl、JSON.parseObject.*Type、toJSONString.*Skip,重点审查泛型解析和大对象序列化逻辑


















