(二十) Java开发者必看:什么垃圾回收(GC)调优? 如何调优?
你刚接手了一个Java项目,线上环境隔三差五就出现卡顿,监控面板上的Full GC次数像心电图一样频繁跳动。这时候千万别急着给服务器扩容,可能只是垃圾回收(GC)在跟你玩捉迷藏。我们团队去年处理过类似情况,某电商平台在双十一期间通过GC调优硬是把服务器成本砍掉30%,今天就带你看看他们是怎么做到的。
一、给内存停车场划好停车位
想象你家小区只有50个停车位,但物业允许200辆车同时进入。结果就是车主们不停转圈找车位,物业的拖车(GC)不得不频繁出动拖走违规车辆。JVM的堆内存设置就像这个停车系统,-Xmx(最大堆内存)和-Xms(初始堆内存)就是停车场的总容量和初始开放区域。
某社交App曾把堆内存设为4G固定值,结果每次大促期间GC停顿超过2秒。当他们根据实际对象存活情况调整为动态范围(-Xms2g -Xmx8g),就像给停车场加装了伸缩护栏,高峰期GC频率直接腰斩。记住用jstat -gcutil持续观察老年代使用率,当长期超过70%就该考虑扩容了。
二、选对垃圾清理工类型
小区保洁分扫地机器人、电动扫地车、人工清洁队不同配置,GC收集器选择也是这个道理。Parallel Scavenge像批量作业的扫地车,适合吞吐量优先的批处理系统;CMS如同带着吸尘器的机动小队,适合要求低延迟的在线服务;G1则是装备GPS的智能清洁系统,能预测哪些区域最需要打扫。
我们给游戏服务器做优化时,原本用CMS遇到内存碎片问题,每次大版本更新后都要重启服务。把收集器更换为G1,再配合 -XX:MaxGCPauseMillis = 200这一参数,就像给清洁队配置实时调度系统一般,角色瞬移时的卡顿从800ms急剧减少到150ms以内。若要进行日志分析,请记住使用“-XX:+PrintGCDetails”。若在日志中看到“concurrent mode failure”字样,便应当考虑更换收集器了。
三、为临时工设定合理的排班表。
年轻代就像快递公司的临时分拣区,对象在这里活不过两轮GC就会被清理。调整-XX:NewRatio参数就像决定分拣区占整个仓库的比例,设成2表示分拣区占1/3面积。某物流系统曾因NewRatio默认值3导致频繁Minor GC,调整后就像把临时分拣区扩大一倍,包裹分拣效率提升40%。
Survivor区的-XX:SurvivorRatio=8参数更要留意,这相当于给分拣区里的待复查包裹留多少暂存位。我们优化过一个日活百万的资讯App,发现Survivor区经常爆满导致对象直通老年代。把比例从8调整到6,相当于给复查区加了两排货架,老年代GC次数周环比下降65%。用jmap -histo:live命令定期查看对象年龄分布,存活超过15次的对象就该考虑晋升阈值调整了。
某在线教育平台把这些技巧组合使用后,高峰期API响应时间从1.2秒降到200毫秒,服务器数量从200台缩减到120台。记住调优就像中医把脉,先用jvisualvm连上生产环境把个脉,看看内存曲线是阴虚(内存泄露)还是火旺(GC过频)。调参时每次只改一个参数,就像抓中药要一味味试,用GC日志分析工具对比调整前后的停顿时间和吞吐量变化。当你发现GC日志里的"Times"开始变得规律平稳,那就是程序进入最佳状态的信号。