【Java踩坑笔记】50_一次生产OOM排查实录:从告警到根因

📅 2026/7/17 21:40:43
【Java踩坑笔记】50_一次生产OOM排查实录:从告警到根因
50 | 一次生产 OOM 排查实录从告警到根因摘要本文用完整的排查流程带你走一遍生产 OOM 的标准排查路径告警 → 保留现场 → 分析堆快照 → 修复 → 验证。一、现象凌晨 3 点线上告警java.lang.OutOfMemoryError: Java heap space。服务重启后恢复正常但 2 小时后又 OOM 了。二、排查步骤标准流程步骤 1保留现场最重要# ✅ OOM 时自动 dump 堆快照在 JVM 参数里加-Xmx2g-XX:HeapDumpOnOutOfMemoryError-XX:HeapDumpPath/data/logs/heap.hprof# 如果没加这个参数用 jcmd 手动 dump越快越好jcmdpidGC.heap_dump /data/logs/heap.hprof为什么重要重启后现场就没了后面只能靠猜。步骤 2看 OOM 类型# 查看日志里的 OOM 类型grepOutOfMemoryError/data/logs/app.log# 常见类型# Java heap space → 堆满了# Metaspace → 元空间满了类加载泄漏# Direct buffer memory → 堆外内存满了# Unable to create new native thread → 线程数超了步骤 3用 MAT 分析堆快照用 Eclipse MAT 打开 heap.hprof → 看 Leak Suspect泄漏嫌疑对象 → 看 Histogram按类名统计实例数 → 看 Dominator Tree占内存最大的对象树本次排查结果Leak Suspect: java.util.ArrayList ← com.example.OrderCache.pendingOrders ← 有 200 万个 Order 对象占 1.8GB步骤 4定位代码// ❌ 根因OrderCache 的 pendingOrders 只加不删ComponentpublicclassOrderCache{privatestaticfinalListOrderpendingOrdersnewArrayList();publicvoidadd(Orderorder){pendingOrders.add(order);// ✅ 加了// ❌ 但没有任何地方删除}}步骤 5修复 验证// ✅ 修复用 WeakReference 或定期清理pendingOrders.removeIf(order-order.isProcessed());发版后观察堆使用量不再持续增长。三、OOM 排查清单速查表步骤命令 / 工具目的保留现场-XX:HeapDumpOnOutOfMemoryErrorOOM 时自动 dump保留现场手动jcmd pid GC.heap_dump没加参数时手动 dump看 OOM 类型grep OutOfMemoryError app.log确定是哪个区域 OOM分析堆快照Eclipse MAT找泄漏对象看 GC 日志-Xlog:gc*JDK 11看 GC 频率和耗时监控趋势Grafana JMX Exporter提前发现内存增长趋势四、最佳实践#✅ 预防 OOM 的 5 条铁律#必须加-XX:HeapDumpOnOutOfMemoryErrorOOM 时自动保留现场开 GC 日志-Xlog:gc*JDK 11方便事后分析用 Prometheus Grafana 监控堆使用趋势提前告警定期用jcmd做堆快照对比是否有增长趋势写单测时用-Xmx128m跑尽早发现内存问题# 快速判断是哪种 OOM## 看进程占用的总内存ps-ppid-orss# 如果总内存远大于 -Xmx说明是堆外 OOMMetaspace / DirectBuffer五、小结#OOM 排查第一步保留现场加-XX:HeapDumpOnOutOfMemoryError用Eclipse MAT分析堆快照找Leak Suspect常见根因集合只加不删、缓存无限增长、连接池泄漏预防优于排查GC 日志 监控趋势 定期堆快照下一篇预告接口响应突然变慢我们这样定位到了死锁——jstack分析线程堆栈找到死锁和慢方法。