主题
Java 服务出现内存持续上涨、频繁 Full GC 或 OOM 时怎么排查?
题型:问题排查 · 先区分堆、元空间、直接内存和系统内存,再做引用链分析。
排查顺序
- 确认现象和影响范围:按实例、版本、流量和时间确认是堆占用上升、进程 RSS 上升、GC 停顿变长还是已经 OOM;保留 OOM 类型、GC 日志、容器事件和最近发布记录。
- 先做线上止损:摘除异常实例、限流或关闭高内存功能,必要时在保留诊断材料后滚动重启。不能在所有实例同时执行高开销 Heap Dump,也不能让自动重启覆盖唯一现场。
- 分内存区域定位:堆问题看 Full GC 后的占用基线和对象分配、晋升;元空间看类数量与 ClassLoader;直接内存看 NIO、Netty buffer 和 Native Memory Tracking;线程过多还会消耗本地栈,容器中还要核对堆外开销与 cgroup 上限。
- 分析对象与引用链:对比两个时间点的类实例数和 retained size,查看 dominator tree、到 GC Roots 的路径,重点检查无界缓存、静态集合、ThreadLocal、监听器、队列积压、未关闭资源和自定义 ClassLoader。
- 永久修复并验证:为缓存、队列和批任务设置容量与生命周期,确保 ThreadLocal 清理、资源关闭和类加载器可卸载;用回放或长稳压测确认 Full GC 后基线稳定,业务延迟和吞吐没有回退。
- 复盘监控:增加老年代回收后占用、增长斜率、类加载数量、直接内存、队列长度和 OOM 类型告警,并保留可控的自动取证策略。
重试与副作用判断
OOM 后进程中的请求可能处于结果未知状态。纯查询通常可以由上游按超时策略重试;写请求必须依赖幂等键、事务结果查询或业务对账,不能因为实例重启就直接重放。Heap Dump、JFR 和 NMT 都有资源成本,应控制触发条件、次数和存储位置。
易错点
- RSS 上涨不一定是 Java 堆泄漏,可能来自直接内存、线程栈、内存映射或本地库。
- Heap Dump 中对象数量多不等于泄漏,要看回收后趋势、retained size 和异常引用链。
- 频繁 Full GC 也可能是堆配置、显式 GC、元空间不足或分配压力,不应直接下结论为泄漏。