You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

过度激进的垃圾回收占用CPU问题排查求助

解决JVM突发GC疯狂占用CPU的问题

兄弟,这种GC突然雪崩、把CPU榨干的情况我之前踩过坑,太闹心了!先来帮你拆解下你遇到的核心问题,再给你实用的排查和优化方向:

先明确核心现象

从你给的日志和描述来看:

  • 应用启动后GC完全正常,Young GC间隔20-30秒一次,回收后Young Gen留10-12MB存活对象,完全符合预期
  • 随机时间点后,GC直接疯了——每秒触发好几次,而且Young Gen回收后只剩几百KB,单个JVM线程把CPU吃到100%+,应用直接罢工
  • 当前用的是ParallelGC,堆初始256MB,最大2GB

先搞懂为什么会这样

从异常GC日志里能看到两个关键细节:

  1. 每次GC前Young Gen都被填满了(比如刚回收完是10MB,转眼就涨到70MB),说明对象分配速度快到离谱,Young Gen刚空出来就被瞬间填满
  2. 回收后几乎没存活对象,Old Gen大小也没怎么变——说明这些都是临时对象,本该在Young Gen里就被回收,但架不住分配速度太快,GC刚结束就得立刻再跑

而且ParallelGC是吞吐量优先的收集器,遇到这种情况会不断尝试扩容Young Gen(你日志里Young Gen的大小一直在涨:73728K→81408K→92160K...),但如果分配速度远超GC回收速度,扩容也赶不上,就会陷入“GC→填满→GC”的死循环,把CPU直接占满。

另外你说JVM只用到一个CPU——ParallelGC的GC线程数默认是(CPU核心数*5/8),你服务器是2核,算下来就是1个GC线程,这就导致GC线程本身成了瓶颈,哪怕单次GC耗时短,架不住频率太高,直接把这个线程的CPU拉满。

实用排查步骤

1. 异常发生时立刻抓现场

当GC炸锅的时候,别犹豫,马上执行这几个命令:

  • jstack <你的应用PID>:抓线程栈,看看哪个线程在疯狂创建对象(大概率是业务线程,比如处理批量任务的)
  • jmap -dump:format=b,file=heap_dump.hprof <你的应用PID>:抓堆快照(注意:这个操作会暂停应用几秒,尽量在测试环境复现后操作,或者业务低峰期搞)
  • jstat -gc <你的应用PID> 1000:每秒输出一次GC状态,实时看Eden/Survivor/Old Gen的变化,确认是不是Young Gen被瞬间填满

2. 检查是否有突发流量或定时任务

这种随机触发的情况,90%和突发业务流量或者定时任务启动有关:

  • 去看你的业务监控,异常发生时QPS是不是突然暴涨?比如某个接口被大量调用
  • 查一下服务器的定时任务(比如crontab)、应用内部的定时任务(比如Spring Schedule),是不是刚好在异常时间点执行了批量处理、数据导出这类会创建大量临时对象的任务

3. 分析堆快照找元凶

拿到堆快照后,用VisualVM或者MAT(Memory Analyzer Tool)打开,重点看:

  • 哪些对象数量最多?是不是大量字符串、ArrayList这类临时对象?
  • 这些对象是哪个线程创建的?对应到代码里的哪块逻辑?
  • 有没有循环创建对象的逻辑?比如在for循环里每次都new一个大对象,没有复用

临时缓解&长期优化建议

临时救急措施

  • 先调整GC线程数:在JVM参数里加-XX:ParallelGCThreads=2,让GC用满两个CPU核心,这样单次GC速度更快,可能能跟上对象分配的速度
  • 调大Young Gen的初始大小:比如加-XX:NewRatio=2(让Young Gen占堆的1/3,2GB堆的话就是约660MB),或者直接指定-XX:NewSize=512m -XX:MaxNewSize=512m,减少Young GC的频率

长期优化方向

  • 找到代码里的对象分配热点:如果是定时任务导致的,看看能不能优化任务逻辑,比如分批处理,减少单次创建的对象数量;如果是接口流量导致的,看看能不能缓存重复对象,或者用对象池复用
  • 考虑调整GC收集器:如果你的应用对延迟有要求,ParallelGC这种吞吐量优先的可能不太适合,可以试试G1GC(加-XX:+UseG1GC),G1在应对突发大对象分配时的表现会更稳定
  • 监控JVM的分配速率:用jstat -gcutil <PID> 1000计算分配速率=(Young Gen总大小)/ Young GC间隔,对比正常和异常时的数值,就能明确是不是分配速率暴涨导致的

最后再提个细节

从你异常阶段的日志来看,Old Gen的大小几乎没变化,说明几乎没有对象晋升到老年代——这进一步坐实了是瞬时大量临时对象的分配导致的问题,不是内存泄漏,而是短时间内对象创建速度远超GC回收能力。

内容的提问来源于stack exchange,提问作者Jeremy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:41:20