集合类内存泄漏典型表现为堆内存持续上涨、Full GC后老年代回收率极低(仅降1–5%),GC日志显示“PSOldGen: X→Y (Z)且Y接近Z”;根源多为static集合或Spring单例Bean持有大集合,需通过MAT分析GC Roots定位强引用源,并检查清理机制、过期策略、引用类型及对象冗余引用,修复后须压测验证。

集合类内存泄漏最典型的表现是:堆内存持续上涨,Full GC 后老年代回收率极低(
看监控,确认是不是集合类泄漏
不是所有内存增长都是泄漏。重点盯住这几个信号:
- 应用启动后,堆内存使用率呈单调上升趋势(非锯齿状波动)
- 老年代占用长期 >85%,且每次 Full GC 后仅下降 1–5%,甚至不降
- GC 日志中频繁出现 “PSOldGen: X->Y (Z), Y 接近 Z”(说明几乎没回收)
- 对应代码中存在 static Map/List/Set,或长生命周期对象(如 Spring Bean)里持有大集合
抓堆快照,找到“吃内存”的集合实例
用 JVM 参数开启自动 dump:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump/
或在疑似高峰时手动触发:
jmap -dump:format=b,file=heap.hprof
用 Eclipse MAT 打开后,按以下顺序操作:
- 打开 Histogram → 点击类名排序 → 找出 HashMap、ConcurrentHashMap、ArrayList、HashSet 实例数最多、Shallow Heap 最大的几项
- 右键目标集合 → Merge Shortest Paths to GC Roots → exclude all phantoms/weak/soft refs(只保留强引用链)
- 查看谁在持有着这个集合——大概率是 static 字段 或 Spring 单例 Bean 的成员变量
查代码,判断集合是否该清空或换结构
定位到具体类和字段后,重点检查四件事:
- 有没有清理入口? 如 CacheManager.put() 有,但没配 remove()/clear()/evict() 方法
- 有没有过期机制? 静态 Map 存 Session、用户数据却没 TTL 或 LRU 策略
- 是不是误用强引用缓存? 本该用 WeakHashMap 或 Caffeine 缓存,却用了 HashMap
- 集合里的对象本身是否还被其他地方引用? 比如 List.add(new BigObject()),但 BigObject 又被静态监听器持有着
验证修复是否生效
改完别急着上线,本地或预发做三件事:
- 用 VisualVM 或 JConsole 连上进程,反复执行触发逻辑(如模拟 100 次缓存写入),观察老年代是否回落
- 加日志:在 put/remove 方法里打印集合 size,确认增删平衡
- 压测 30 分钟以上,对比修复前后 GC 频率与单次耗时











