gc调优核心是明确吞吐量(用户代码时间占比,目标≥95%)与延迟(p95/p99 stw时间,需匹配sla),依赖gc日志和jstat等工具采集真实数据,按目标选回收器:吞吐优先用parallel gc,低延迟用g1或zgc,并紧盯新生代大小、晋升阈值和元空间等关键参数微调验证。

监控和调优垃圾回收器的吞吐量与延迟,核心在于明确目标、采集真实数据、针对性选型与参数微调。不能靠猜测,必须依赖可观测性数据驱动决策。
明确吞吐量与延迟的实际含义
吞吐量不是“系统快不快”,而是“CPU有多少时间真正跑你的业务代码”。公式很直接:吞吐量 = 用户代码执行时间 /(用户代码执行时间 + GC时间)。比如100秒里GC占了3秒,吞吐量就是97%——生产环境通常要求不低于95%,高负载批处理服务建议压到98%以上。
延迟也不是“一次GC花了多久”,而是“用户请求被STW卡住的最坏体验”。一次200ms的停顿对后台任务可能无关紧要,但对支付接口就可能触发超时熔断。关键要看P95/P99停顿时间是否稳定落在业务SLA内(如≤100ms)。
用真实日志和工具做基础监控
不开启GC日志,一切调优都是空中楼阁。推荐组合参数(JDK 8–17通用):
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- -XX:+PrintGCDetails -XX:+PrintGCDateStamps:记录每次GC类型、耗时、各代内存变化
- -Xloggc:/var/log/jvm/gc-%t.log:按时间戳生成日志文件,避免覆盖
- -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M:自动滚动,防磁盘打满
-
jstat -gc -h10
1000 :实时看YGC/FGC频次、Eden/Survivor/Old使用率,发现“频繁YGC但Old增长慢”说明对象生命周期短,“FGC陡增”则大概率存在内存泄漏或大对象晋升
把日志丢进GCeasy或GCViewer,它会直接算出吞吐量百分比、平均/最大停顿时间、GC频率趋势图——这是所有后续动作的起点。
按目标选收集器,别硬套默认配置
没有“最好的收集器”,只有“最适合当前目标的收集器”:
-
要吞吐量优先(如离线ETL、报表生成):用Parallel GC。加
-XX:+UseParallelGC,再配-XX:MaxGCPauseMillis=300(它会尽量满足,但不强求),堆大小设为固定值(-Xms4g -Xmx4g)避免扩容抖动 -
要低延迟且堆在4–64GB之间(如电商API、风控引擎):选G1 GC。启用
-XX:+UseG1GC,设目标停顿-XX:MaxGCPauseMillis=200,并调-XX:InitiatingHeapOccupancyPercent=45(老年代占用45%就启动并发标记,防突增Full GC) -
堆超32GB又要求毫秒级停顿(如实时风控、高频交易):ZGC(JDK 11+)或Shenandoah(JDK 12+)。加
-XX:+UseZGC即可,它几乎不依赖人工调参,但需确认应用无JNI深度侵入或未兼容的第三方库
调参要盯住关键变量,不是堆越大越好
很多问题出在“调了参数却没验证效果”。重点盯三个可量化变量:
-
新生代大小:G1不用设
-Xmn,它动态调整;Parallel GC建议设为堆的1/2~2/3(如-Xms8g -Xmx8g -Xmn5g),避免YGC太频繁 -
对象晋升阈值:
-XX:MaxTenuringThreshold=15是默认值,但如果监控发现大量对象在Survivor区来回复制10次才进Old,说明SurvivorRatio偏小或对象生命周期长,可适当调高阈值或加大Survivor空间 -
元空间保护:加
-XX:MaxMetaspaceSize=512m,防止动态类加载(如Spring Boot DevTools、热更新框架)撑爆本地内存导致FGC
每次改参后,至少运行2个完整业务周期(比如高峰期+低峰期),用同一份GC日志对比吞吐量变化和P99停顿波动——下降5%吞吐量但P99停顿从300ms降到80ms,对交互式系统就是成功。










