智汇百科
霓虹主题四 · 更硬核的阅读氛围

JVM垃圾回收机制常见问题与排查思路

发布时间:2026-01-04 12:50:32 阅读:295 次

JVM垃圾回收机制常见问题与排查思路

线上服务突然变慢,接口响应时间从几十毫秒飙到几秒,甚至出现超时。查看系统监控,CPU使用率并不高,数据库也没异常。这时候,别急着查业务代码,很可能问题出在JVM的垃圾回收上。

很多开发者对JVM垃圾回收(GC)机制了解停留在“自动回收不用的对象”这种模糊概念上。可一旦系统频繁Full GC,服务卡顿,日志里一堆GC日志刷屏,就容易慌神。其实,只要理清楚GC的基本机制和常见症状,排查起来并不复杂。

先搞明白:JVM内存是怎么分的

JVM把堆内存主要分成年轻代(Young Generation)和老年代(Old Generation)。新创建的对象大多分配在年轻代的Eden区。当Eden区满了,就会触发一次Minor GC,清理不再使用的对象。活下来的对象会被挪到Survivor区,经过几次回收还活着,最终进入老年代。

老年代满了,就会触发Full GC。Full GC耗时更长,会暂停所有应用线程(Stop-The-World),这就是为什么服务会卡住。

看GC日志,像看体检报告

开启GC日志是排查的第一步。启动参数加上:

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log

日志里常见关键词:GC、Pause、CMS、G1、Concurrent Mode Failure、Allocation Failure。比如看到大量"Full GC (Allocation Failure)",说明老年代没空间了,新对象放不进去,只能强刷整个堆。

如果发现Minor GC频繁,但每次回收后可用内存依然很小,可能是有内存泄漏,也可能是Eden区设得太小。反过来,如果Minor GC间隔很长,但一触发就停很久,可能是对象晋升太快,一下子把老年代撑爆。

不同GC策略的表现差异

用Parallel Scavenge(默认吞吐量优先)时,系统可能运行挺稳,但一旦Full GC,停顿明显。适合后台批处理任务,对延迟不敏感。

CMS(Concurrent Mark Sweep)本意是减少停顿时间,但容易出现"Concurrent Mode Failure"——并发清理还没完,老年代又满了,被迫退化成Serial Old单线程回收,反而更慢。

G1收集器主打可预测停顿,把堆拆成多个Region,优先清理垃圾最多的区域。但如果日志里看到"Full GC",说明G1也扛不住了,可能是对象分配速度远超回收能力,或者Mixed GC没及时跟上。

实战排查步骤

第一步,用jstat看实时GC情况:

jstat -gcutil <pid> 1000

观察YGC(年轻代GC次数)、YGCT、FGC(Full GC次数)、FGCT这些指标。如果FGC持续增长,FGCT累计时间越来越长,基本可以断定是老年代有问题。

第二步,抓内存快照:

jmap -dump:format=b,file=heap.hprof <pid>

用MAT或JVisualVM打开分析,重点看哪些类的实例最多,占用内存最大。常见元凶:缓存没设上限、静态集合不断添加、IO流没关闭导致关联对象无法释放。

第三步,检查JVM参数。比如-Xms和-Xmx设置不一致,导致堆动态扩容,容易触发GC;或者年轻代比例太小,对象早早被推到老年代。

曾经有个案例,一个服务每小时都来一次Full GC,查下来是因为定时任务生成的临时大对象太多,而MaxTenuringThreshold设得太高,Survivor区根本装不下,直接进老年代。调低阈值,问题缓解。

GC不是玄学,它只是在告诉你:内存不够用了,或者对象活得太极端。学会读日志,看指标,结合代码逻辑,大多数GC问题都能定位到根子上。