1. 内存泄漏现象识别
内存泄漏在应用运行中会逐渐恶化,早期往往表现为三种可观测现象:GC 日志异常、堆内存持续增长、应用响应变慢。这三者构成一条因果链,破解它就能找到泄漏源头。
1.1 现象因果链
现象因果链: 泄漏对象无法被回收 → 堆内存增长 → 触发更多 Full GC → GC 暂停时间延长 → 应用响应变慢 → 最终 OutOfMemoryError 或容器 OOM Kill。
1.2 GC 日志异常关键指标
| 指标 | 健康应用 | 内存泄漏应用 |
|---|---|---|
| Young GC 频率 | 稳定(例:每 5~10 秒一次) | 快速上升(例:每 1~2 秒一次) |
| Full GC 频率 | 极低(数天一次) | 频繁(分钟级甚至秒级) |
| GC 后堆占用率 | 明显下降(通常 <40%) | 下降不明显甚至持续上升 |
| GC 暂停时间 | <200ms(正常范围) | 持续 >1s,甚至 >10s |
| 日志模式 | GC (Allocation Failure)... 堆: 100M->40M |
Full GC (Metadata GC Threshold)... 堆: 800M->780M |
1.3 堆内存持续增长判断规则
- 启动后一段时间内:堆内存曲线先上升后稳定(正常);若持续线性上升,高度怀疑泄漏。
- Full GC 后:健康应用堆占用率回落至基线;泄漏应用回落幅度极小或完全不回落。
- 对比多个时间窗口:取 10 分钟、30 分钟、1 小时三个时间点的堆快照,计算 “回收后占用率”,若逐窗口递增则判定为泄漏。
1.4 应用响应变慢的典型表现
- 用户请求延迟从 <50ms 上升至 >500ms,甚至超时。
- 线程池队列积压,CPU 使用率虽低但线程大量阻塞在 GC 停顿中。
- 负载不高时出现 Connection Timeout、SocketException 等网络超时错误。
1.5 快速自查清单
- ☐ 观察监控面板(如 Grafana):堆曲线是否为 “上升—平台—上升—平台” 模式?
- ☐ 执行
jstat -gcutil <pid> 1000 10查看 Full GC 次数是否在 10 秒内增加? - ☐ 检查最近一次 Full GC 后的堆占用是否接近最大值(例如 90% 以上)?
- ☐ 压测场景下响应时间是否与 GC 暂停时间正相关?
操作提示: 在容器环境中,推荐开启
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/gc.log,并将 GC 日志同步到集中日志系统以便时间维度分析。
易犯错误: 不要只依赖“应用重启后恢复正常”来判断泄漏 —— 有些泄漏需要积累数小时才显现,重启只是暂时重置堆状态。一定要采集持续运行的 Post-GC 堆占用曲线。
1.6 章节小结
第 1 章建立了内存泄漏的现象识别基线:GC 日志异常、堆内存持续增长、应用响应变慢是三个核心观测维度。利用本章给出的指标表格和故障树,开发与运维人员可以快速判断是否存在泄漏,并为后续数据采集(第 2 章)提供明确入口。