背景
通过监控发现,接口响应慢主要是 P99
耗时高引起的。怀疑与 GC 有关。
负载 | 单机QPS |
---|---|
高负载 | >1000 |
中负载 | 500~600 |
低负载 | <200 |
默认
JDK 8 默认的垃圾回收器是 Parallel GC。即 Young 区采用 Parallel Scavenge,老年代采用 Parallel Old 进行收集。该配置主要特点是吞吐量优先,一般适用于后台任务型服务器。比如批量订单处理、科学计算等对吞吐量敏感的,但对时延不敏感的场景。如果是交互类的,对时延非常敏感,因此,就不适用于这款垃圾回收器。
分代假说
JVM 基于分代假说,提供基于不同代使用不同类型的垃圾回收算法。绝大部分的对象是朝生夕死的,很少有对象经过多轮 GC 仍能存活。如果仍存活,它们晋升到老年代中。但是,分代假说并非银弹,在高并发场景下,会有大量朝生夕死的对象被创建,进而填满整个 Young 区。在监控画面显示就是 Young GC 次数非常频繁。而且,还会引起本应被 Young GC 回收的对象过早晋升,增加 Full GC 的频率。JDk 8 默认使用 Parallel Old,基于标记-整理算法实现,但是它无法与用户线程并行执行,导致服务长时间停顿,可用性下降。
当前主流垃圾回收器
垃圾回收器组合 | 优点 | 缺点 |
---|---|---|
Parallel Scavenge Parallel Old |
吞吐量优先,后台任务型服务适合 | 对时延敏感类应用不友好 |
ParNew + CMS | 经典低停顿收集器,大多数商用、延时敏感的服务在使用 | |
G1 | JDK 9 默认收集器。堆内存较大时(6GB 以上)表现出较高吞吐量和低时延特性 | 堆空间占用较多, |
ZGC | JDK 11 推出一款低延迟垃圾回收器 |
调优 ParNew + CMS
调优原则
- 元数据区 Metaspace 大小一定要指定。通过
jstat -gc
查看。 Young
区并非越大越好。不同的垃圾回收器组合形式。
- Parallel Scavenge + Parallel Old
- ParNew + CMS。
- Young 区和 Old 区分配。
-
压测结论
ParNew
+CMS
的组合远远好于Parallel Scavenge
+Parallel Old
。Young
区提升 0.5 倍表现最佳。P99
延时降低 50%,Full GC 累积耗时减少 88%。Young GC 次数减少 23%,Young GC 累积耗时减少 4%。还是出现问题
我们需要明确 CMS 收集器何时会触发 Full GC 执行。
对于 CMS,采用的收集算法是Mark-Sweep-Compact
,GC 种类有两种:Background GC
。一个周期性任务,由 JVM 常驻线程定时扫描老年代的使用率。当使用率超过阈值时触发 Full GC,采用Mark-Sweep
方式。由于没有Compact
这种耗时操作,且可以与用户并行执行,所以这一步相对来说耗时较低。GC 日志出现一次CMS Initial Mark
就意味着发生了一次 Background GC。Foreground GC
(前台 GC)。这种 GC 是真正意义上的 CMS 的 Full GC。采用 Serial Old 或 Parallel Old 进行收集。出现频率较低,一旦出现,往往会造成机器较大的停顿。触发的场景总结如下:System.gc
jmap -histo:live pid
- 元数据区域空间不足
- 晋升失败。GC 日志出现:
ParNew(promotion failed)
- 并发模式失败。GC 日志出现:
concurrent mode failure
所以,根据监控信息显示,不难推断,毛刺的出现多半是由于对象晋升失败或并发模式失败而导致 Foreground GC
,深层本质是多次 Background GC
造成老年代内存碎片严重,无法找到一块连续的内存存储新晋升的对象。
相关调优参数
Background GC
触发 Full GC 的阈值。由-XX:CMSInitiatingOccupancyFraction
、-XX:+UseCMSInitiatingOccupancyOnly
两个参数控制。默认首次为92%
,后续会根据历史情况进行预测,动态调整。我们应设置一个相对合理的值。目标:不使 GC 过于频繁,又可以降低晋升失败或并发模式失败的概率。这就可大大缓存毛刺产生的频率。按经验数据,75%、80%
是折中值,我们可以进行测试。结果是75%
优于80%
。-XX:+CMSScavengeBeforeRemark
在重新标记前先执行一次新第一代 GC。
参数:
-Xms4096M -Xmx4096M -Xmn1536M
-XX:MetaspaceSize=256M
-XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:+CMSScavengeBeforeRemark
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
过程回溯
评估必要性 -> 确定目标 -> 问题分析/方案制定 -> 验证 -> 结果验收 -> 完成