一、那个半夜的报警
某日生产环境突然大面积超时,监控显示 JVM 堆内存 100%,Full GC 每秒一次,服务几乎停滞。本文复盘完整排查链路。
二、第一步:确认是不是 OOM
# 看进程内存
top -p $(pgrep -f app.jar)
# 看 GC 频率
jstat -gcutil $(pgrep -f app.jar) 1000
EU/OU 接近 100% 且 FGCT 持续增长,基本锁定堆内存问题。
三、第二步:抓堆转储
# 自动在 OOM 时 dump(推荐提前加)
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof
# 或手动抓
jmap -dump:live,format=b,file=/tmp/heap.hprof $(pgrep -f app.jar)
四、第三步:用 MAT 分析
打开 Eclipse MAT,看 Dominator Tree,谁占的内存最多一目了然。本次发现 ConcurrentHashMap 里塞了几百万条缓存对象,且从不过期。
五、根因:缓存没设上限
代码里用普通 HashMap 做本地缓存,key 随时间无限增长。修复:
// 改用 Caffeine,带容量和过期
Cache<String, Data> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
六、复盘清单
- 本地缓存必须设容量上限和过期策略。
- 容器内存限制要合理,避免被 OS OOM Killer 误杀。
- 提前开
HeapDumpOnOutOfMemoryError,出事能秒定位。 - 加监控:堆使用率超 80% 就告警,别等故障。
结语
每次 OOM 都是一次免费的教学。把排查过程记下来,下次同类问题十分钟解决。




