java垃圾回收异常需通过jvm gc日志定位,而非系统日志;须配置对应jdk版本的gc日志参数,识别full gc(system)、concurrent mode failure等关键信号,并结合jstat实时验证、jmap抓堆分析引用链,最终从代码层排查static缓存、threadlocal未清理等问题。

Java 垃圾回收异常不是靠查系统日志(如 Linux 的 /var/log/messages 或 Windows 事件查看器)定位的,真正有效的是JVM 自身生成的 GC 日志——它才是反映内存行为的第一手证据。
看 GC 日志,不是看操作系统日志
GC 异常(比如频繁 Full GC、STW 过长、GC overhead limit exceeded)不会写进系统日志,只会出现在 JVM 启动时配置的 GC 日志文件里。
必须确保启动参数中已开启:
- JDK 8/9/10:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M
- JDK 11+(推荐统一用
-Xlog):-Xlog:gc*,gc+heap=debug,gc+ref=debug,time,uptime,tags:file=/path/to/gc.log:filecount=5,filesize=10M
关键要识别日志里的三类信号:
- 出现
Full GC (System)→ 说明代码或框架调用了System.gc() - 出现
Concurrent Mode Failure(CMS)或to-space exhausted(G1)→ 老年代或混合回收失败,可能晋升过快或碎片严重 - 连续多行
GC (Allocation Failure)且间隔
结合 jstat 实时验证日志发现
光看日志是回溯,需用 jstat 实时确认是否正在发生异常:
jstat -gcutil <pid> 200 10 # 每200ms采样一次,共10次</pid>
重点关注:
-
E列在 90% ↔ 10% 之间剧烈跳变 → 内存抖动 -
O列持续上升、FGC计数加速增长 → 老年代压力大,可能泄漏或晋升过多 -
YGCT/FGCT占比突增(例如 10 秒内 GC 耗时超 2 秒)→ STW 已影响服务
定位到具体对象,得靠堆镜像和引用链
当怀疑泄漏或对象堆积时,在 GC 频繁阶段抓一次堆:
jmap -dump:format=b,file=heap.hprof <pid></pid>
用 VisualVM、JProfiler 或 MAT 打开后:
- 按 Retained Heap 排序,找前几的类(不是实例数多就危险,是“谁一直拿着它”)
- 点开可疑类 → 查 Reference Chain → 看最上层强引用来源
常见根因包括: -
static Map缓存未设上限也未清理 -
ThreadLocal使用后没调remove() - 监听器注册了但没反注册,随时间越积越多
- 流、连接、Buffer 没 close,关联的字节数组长期驻留
不要忽略代码层显式触发点
有些 GC 异常是人为引发的:
- 某些 SDK、监控脚本、RMI 服务会静默调用
System.gc() - 日志里看到
Full GC (System)多次出现,就该搜代码和依赖:grep -r "System\.gc" ./src ./lib/
- 生产环境可加
-XX:+DisableExplicitGC临时禁用,再观察是否出问题——若无异常,说明原调用纯属冗余
GC 异常的本质,是内存使用模式和回收机制不匹配。日志指方向,工具验假设,代码定根因。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











