线上内存泄漏需三线并进:一、立即止血,通过扩容堆内存、强制full gc、限流降载稳住服务;二、5分钟内定位,分析gc日志、抓堆快照、查retained heap top对象;三、聚焦静态集合堆积、资源未关闭、监听器未注销、threadlocal误用四类高频原因;四、灰度验证+监控告警+兜底机制闭环收尾。

线上系统出现内存泄漏,不能只等重启——得边稳住服务、边定位根因、边修复上线。应急方案不是“先杀进程再重来”,而是分秒必争的三线并进:保业务、锁问题、清隐患。
一、立即止血:临时缓解内存压力
目标是让服务继续扛住流量,避免OOM崩溃。
-
快速扩容堆内存:若JVM参数可动态调整(如使用容器或支持JMX),临时提高
-Xmx(例如从2G调至4G),同时增大元空间-XX:MaxMetaspaceSize,争取排查时间;注意别超物理内存,否则触发OS OOM Killer。 -
强制触发Full GC:用
jcmd <pid> VM.native_memory summary</pid>确认内存分布后,执行jcmd <pid> VM.run_finalization</pid>和jstat -gc <pid></pid>观察效果;谨慎使用jmap -histo:live <pid></pid>触发一次Full GC(仅限低峰期)。 - 限流降载:通过网关或应用层熔断(如Sentinel规则)限制非核心接口QPS,减少新对象创建,延缓内存增长速度。
二、快速定位:5分钟内锁定可疑方向
不盲目dump,先看现象筛范围。
-
查GC日志特征:确认是否出现
GC overhead limit exceeded或老年代使用率持续>90%、Full GC间隔不断缩短、单次耗时>1s——满足任意两点,基本坐实内存泄漏。 -
抓实时堆快照:用
jmap -dump:format=b,file=heap.hprof <pid></pid>生成dump(生产环境建议加-F强制,但可能短暂STW);若磁盘空间紧张,可用arthas memory --heap-dump直接导出压缩版。 -
看活跃对象TOP榜:用VisualVM或MAT打开dump,按“Retained Heap”排序,重点关注
HashMap$Node、ArrayList、byte[]、自定义缓存类等——它们常是泄漏源头的“藏身地”。
三、精准归因:聚焦高频泄漏场景
80%的线上泄漏集中在以下几类,对照代码逐项排除:
-
静态集合无限堆积:检查所有
static Map/List/Set,确认是否有add无remove、无过期清理逻辑;特别留意工具类、配置缓存、统计计数器。 -
资源未关闭:搜
new FileInputStream、Connection、Statement等关键词,确认是否缺失try-with-resources或finally.close()。 -
监听器/回调未注销:查看
ServletContextListener、EventListener、Spring事件监听器,确认contextDestroyed或销毁方法中是否移除了注册。 -
ThreadLocal误用:检查
ThreadLocal<object></object>声明是否为static,且在业务线程池中未调用remove()——这是线程复用场景下的高危点。
四、验证与收尾:确保修复真正生效
改完代码不是终点,得验证泄漏是否真的停止。
-
灰度发布+监控对比:新版本只上1台机器,用Prometheus+JVM Exporter观察
jvm_memory_used_bytes{area="heap"}曲线——重启后若堆内存能稳定回落并保持平台期,说明修复有效。 - 补监控告警:在APM(如SkyWalking、Arthas Dashboard)中配置“老年代使用率>85%持续5分钟”告警,把被动救火转为主动预警。
-
加兜底机制:对必须用静态缓存的模块,强制加上容量上限(如Caffeine的
maximumSize)和自动过期(expireAfterWrite),避免再次失控。
应急不是修完就完事,而是把这次泄漏变成下次不再发生的防线。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











