高并发内存泄漏需快定位、准切断:先用jstat观察GC异常(OU持续上涨、FGCT陡增),再用jmap -dump:live抓堆快照,MAT通过Histogram、Dominator Tree和Leak Suspects定位根因,聚焦静态缓存、ThreadLocal、监听器、内部类、IO资源五类高频问题修复。

高并发场景下内存泄漏更隐蔽、爆发更快,关键在于“快定位、准切断”。不能等OOM才行动,要从GC行为异常入手,用jmap快速抓现场,再用MAT深挖引用链,最后聚焦高频泄漏点修复。
看GC行为,确认是不是泄漏
高并发服务一旦出现内存问题,先别急着dump,用jstat盯住GC趋势:
-
jstat -gc <pid> 2000:每2秒输出一次GC统计,重点关注
OU(老年代使用量)是否持续上涨、FGCT(Full GC次数)是否陡增、FGC(单次Full GC耗时)是否超过1秒 - 如果发现
OU涨了80%以上且Full GC后不回落,FGCT从每天几次变成每分钟多次——基本可断定是内存泄漏,不是临时高峰 - 配合监控看线程数:
jstack <pid> | grep java.lang.Thread.State | wc -l,线程暴涨可能暗示ThreadLocal泄漏或连接未释放
用jmap快速抓取有效堆快照
高并发进程对dump敏感,要避免阻塞业务,选对时机和参数:
- 优先用jmap -dump:format=b,live,file=heap.hprof <pid>:加
live参数只导出存活对象,文件更小、分析更快,适合线上紧急场景 - 避开流量高峰执行;若进程响应慢,可先用jmap -histo <pid> | head -30快速查看Top30类实例数和内存占比,比如发现
java.util.HashMap$Node或byte[]异常多,就立刻dump - 不要用
-dump:format=b,file=...不加live,否则可能导出大量已标记为待回收但尚未清理的对象,干扰判断
用MAT精准锁定泄漏源头
打开hprof后,跳过界面等待,直奔三个关键视图:
立即学习“Java免费学习笔记(深入)”;
-
Histogram + Group by package:找包下实例数突增的类,比如
com.yourcompany.cache.UserContext有上百万实例,就是重点怀疑对象 - Dominator Tree:看“Shallow Heap”和“Retained Heap”差距大的类,Retained Heap越大,说明它持有的子对象越多,越可能是泄漏根因
- Leak Suspects Report:MAT自动生成的报告,通常第一项就是主因,比如“One instance of 'org.apache.tomcat.util.threads.TaskThread' is holding on to ...”,直接点进去看GC Roots路径
聚焦高频泄漏点,针对性修复
高并发环境下,以下五类问题占泄漏案例90%以上,修复要具体到代码习惯:
-
静态缓存无淘汰:把
static Map<String, Object>换成Caffeine.newBuilder().maximumSize(10000).expireAfterWrite(10, TimeUnit.MINUTES) -
ThreadLocal未remove:在线程池场景中,必须在finally块里调用
threadLocal.remove(),不能只靠set(null) -
监听器/回调未注销:注册时保存引用,业务结束时显式调用
eventBus.unregister(this)或listener.close() - 内部类持有外部引用:将匿名内部类改为静态内部类+弱引用传参,或改用Lambda(不隐式持外部this)
-
流/连接未关闭:所有IO操作强制用try-with-resources,数据库连接确保在连接池配置中启用
testOnBorrow和minEvictableIdleTimeMillis
修复后上线,用jstat持续观察3个Full GC周期,确认OU回落、FGCT归稳,才算闭环。不复杂但容易忽略。


















