内存泄漏导致崩溃的核心表现是堆内存持续上涨、Full GC后不回落、最终OOM或被OOM Killer杀掉;排查需按现象确认、进程定位、堆转储分析、代码归因四步闭环推进,依赖jstat、jmap、MAT等工具链,而非猜测。
服务因内存泄漏崩溃,核心表现是内存占用持续上涨、频繁 full gc 后堆内存不回落、最终触发 oom 或被系统 oom killer 杀掉。排查需从现象确认、进程定位、堆内分析、代码归因四步推进,不依赖猜测,靠数据链闭环验证。
确认是否为内存泄漏引发的崩溃
先排除误判:内存泄漏 ≠ 内存溢出(OOM),但它是 OOM 的常见前置原因。
- 查应用日志:搜索 java.lang.OutOfMemoryError: Java heap space 或 Metaspace 等错误;若无,再看是否有 “killed process” 字样 —— 这通常是 Linux 内核 OOM Killer 主动终止高内存进程,说明系统级内存已枯竭。
- 查系统日志:dmesg -T | grep -i "killed process" 或翻阅 /var/log/messages,确认是否出现类似 “Out of memory: Kill process 12345 (my_service) score 890” 的记录。
- 观察趋势:用 top 或 htop 持续观察该服务进程的 %MEM 和 VIRT/RES 值,若 RES(物理内存)随时间单向增长且不显著回落,高度可疑。
定位泄漏进程与内存增长特征
明确“谁在吃内存”以及“怎么吃的”,避免在错误进程上浪费时间。
- 按内存排序查进程:ps aux --sort=-%mem | head -10,重点关注 RES 列(实际物理内存占用)和 CMD 列是否为你服务的 Java 进程(如含 java -jar 或 java -cp)。
- 动态监控增长:watch -n 2 'ps -p <PID> -o pid,ppid,vsz,rss,%mem,comm,args',每2秒刷新一次,直观看到 RSS(物理内存)是否线性爬升。
- 检查 GC 行为:启用 JVM GC 日志(如 -Xloggc:/var/log/myapp/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps),用 GCViewer 或文本分析查看:Full GC 是否越来越频繁?每次 GC 后老年代(Old Gen)使用量是否不降反升?这是泄漏最典型的 GC 特征。
抓取并分析堆转储(Heap Dump)
这是定位泄漏对象的黄金证据,必须在服务还活着、内存明显偏高时主动触发。
- 生成 dump:jmap -dump:format=b,file=heap.hprof <PID>(注意:生产环境慎用,可能暂停应用数秒;更稳妥可用 jcmd <PID> VM.native_memory summary 先看整体分布)。
- 加载分析:用 Eclipse MAT(推荐)或 VisualVM 打开 hprof 文件,重点看:
- Dominator Tree:找出占用堆内存 Top N 的类及其总大小,例如 char[]、byte[]、HashMap$Node 异常多,说明字符串或缓存膨胀;
- Leak Suspects Report:MAT 自动识别的疑似泄漏点,直接给出引用链快照;
- Path to GC Roots:对可疑大对象右键 → “exclude weak/soft references”,看它为何无法被回收——通常暴露静态集合、未关闭资源、ThreadLocal 未清理等根因。
回溯代码与配置中的高危模式
根据堆分析线索,聚焦常见泄漏源,逐项核查:
- 静态集合滥用:全局 static Map/List 是否无限 put 而从不 remove/clear?尤其注意 Spring Boot 中 @Component 类里声明的 static 集合;
- 资源未关闭:InputStream、Connection、Statement、ResultSet 是否都包裹在 try-with-resources 中?或 finally 里显式 close()?数据库连接池配置(如 maxActive、minIdle)是否合理?
- ThreadLocal 泄漏:在线程池场景下,是否在业务逻辑结束后调用 threadLocal.remove()?未 remove 会导致线程复用时旧对象一直挂着;
- 监听器/回调未注销:事件注册后是否有对应反注册逻辑?比如 Netty ChannelHandler 添加后未在 channelInactive 里移除;
- 缓存失控:是否用了无界 ConcurrentHashMap 或 Guava Cache 但没设 maximumSize / expireAfterWrite?Caffeine 是更安全的替代选择。


















