Debian 上 Tomcat 内存泄漏的定位与修复
一 快速判断与日志线索
- 观察 /var/log/tomcat/ 下的 catalina.out、应用日志是否频繁出现 OutOfMemoryError,并结合 GC 日志判断是否因回收无效导致持续增长。
- 典型迹象:
- 堆内存使用持续上升,即使执行 Full GC 后仍无明显回落;
- Full GC 频率增加、单次停顿变长,且回收效果变差;
- 老年代占用在 Full GC 后几乎不降。
- 借助 JVisualVM、Eclipse MAT 做实时监控与堆分析,确认是否存在对象泄漏与引用链问题。
- 若日志或监控提示元空间问题,注意区分 Java 8 之前的 PermGen 与 Java 8 及以后的 Metaspace 两类错误特征。
二 监控与诊断工具链
- 命令行与诊断:
- jstat 观察 GC/类加载;jmap 生成堆转储;jcmd 发送诊断命令;
- Arthas 在线排查对象、线程与内存热点。
- 可视化与深度分析:
- JConsole、VisualVM 通过 JMX 连接 Tomcat,查看堆、线程、类与 MBean;
- Eclipse MAT 对 Heap Dump 做泄漏嫌疑报告与引用链分析。
- 生产可观测性:
- Prometheus + Grafana 采集 JVM/Memory 指标并配置告警;
- Tomcat Manager 查看应用与线程池状态,配合日志平台(如 ELK)做聚合分析。
三 复现与定位步骤
- 打开诊断开关(写入 $CATALINA_BASE/bin/setenv.sh,如文件不存在则创建):
- 堆与元空间:
- -Xms1024m -Xmx2048m(建议与物理内存与容器配额匹配)
- Java 8+:-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m
- Java 7-:-XX:PermSize=128m -XX:MaxPermSize=256m
- GC 日志:
- -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:$CATALINA_BASE/logs/gc.log
- 自动堆转储:
- -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$CATALINA_BASE/logs/heapdump.hprof
- 触发问题并采集证据:
- 用 jstat -gc 观察 YGC/FGC 与内存区变化;
- 用 jmap -dump:live,format=b,file=heap.hprof 或在 VisualVM/MAT 中主动导出 Heap Dump;
- 用 MAT 打开转储,查看 Leak Suspects、支配树(Dominator Tree)与引用链,定位占用最多且无法回收的对象集合。
- 在线诊断补充:
- 用 Arthas 的 dashboard、thread、ognl 等命令快速查看内存、线程与对象分布,辅助定位泄漏路径。
四 常见根因与修复要点
- 资源未关闭:数据库连接、文件/网络流、会话/缓存对象未释放。
- 修复:使用 try-with-resources/finally 确保关闭;对 ThreadLocal 在请求结束时清理;为缓存设置 maximumSize/过期策略。
- 静态集合/缓存膨胀:长期持有对象引用,阻止回收。
- 修复:避免无界缓存;定期清理或采用 弱引用/软引用 策略。
- 类加载器泄漏(热部署/多应用):应用停止后 ClassLoader 与类元数据未卸载。
- 修复:减少动态生成类;避免应用间共享 ClassLoader 状态;升级 Tomcat 版本(已知泄漏点常被修复);必要时重启实例释放元空间。
- 线程与本地内存问题:线程栈过大、线程数过多导致 unable to create new native thread。
- 修复:控制线程池与 -Xss;优化阻塞操作;必要时降低堆以给操作系统预留更多内存用于线程栈。
- 大数据一次性加载:一次性将大文件/大表读入内存。
五 参数建议与运维策略
- 堆与元空间:
- 将 -Xms 与 -Xmx 设为相同值以减少堆扩展抖动;
- Java 8+ 显式设置 -XX:MaxMetaspaceSize,避免无界增长挤占系统内存。
- 垃圾回收器:
- 关注吞吐与停顿,结合负载在 G1/ZGC 等现代回收器中择优;必要时基于 GC 日志 与压测结果微调。
- JMX 远程监控:
- 在 setenv.sh 中按需开启 JMX(如 -Dcom.sun.management.jmxremote 等),用 JConsole/VisualVM 远程巡检。
- 持续观测与告警:
- 以 Prometheus + Grafana 监控 JVM Memory/GC 指标并设置阈值告警;
- 定期审计 GC 日志 与 Heap Dump,在发布前后做对比,尽早发现回归。
- 发布与容量:
- 执行 蓝绿/金丝雀 发布,控制单实例内存峰值;必要时水平扩展与负载均衡分摊压力。