zgc介绍
zgc在JDK15中终于官宣可以投入生产使用,官方博客的话就是稳定,高性能,低延时的gc。
zgc的核心是一个并发垃圾回收器,其设计的目标是:
- 最大停顿时间是几毫秒
- 停顿时间不会随着堆变大而变大
- 堆范围8mb-16tb
当然zgc也有其他很多有用的功能例如:支持NUMA,支持NVRAM申请堆,类数据共享等等
本文主要讨论其在openjdk15和graalVM21和openjdk11中的表现,关于其原理和其他功能不作为重点叙述。
准备工作
一.三个版本的jdk
1.openjdk15 2.graalvm21 3.openjdk11
二.jvm启动jvm参数
1. -XX:ZCollectionInterval =1 -XX:-ZProactive 定时gc,关闭主动gc
2. -XX:ZAllocationSpikeTolerance = 5 不设置定时gc,zgc根据自己算法进行gc 系数为5
三.场景
测试应用:mysql分库分表中间件
1. 1000个连接循环发送请求发送sql至不同版本jdk启动的分库分表中间件(限制单库连接数-产线环境)
2. 2000个并发,用jmeter测试tps发送sql至中间件(不限制单库连接数)
注: 场景 1 验证客户端感知(请求返回时间) 场景3 验证TPS
测试结果:
jdk15(定时gc-场景1)
graalvm21(定时gc-场景1)
jdk11(定时gc-场景1)
jdk15(不定时gc-场景1)
graalvm21(不定时gc-场景1)
jdk11(不定时gc-场景1)
jdk15(定时gc-场景2)
graalvm(定时gc-场景2)
jdk11(定时gc-场景2)
jdk15(不定时-场景2)
graalvm21(不定时-场景2)
jdk11(不定时-场景2)
注:由于是用公司机器进行测试,在进行不定时-场景2测试时,其他应用访问数据库流量变大,所以本次测试结果TPS偏小,后又经过几次测试,tps虽增大,但是仍小于定时-场景2,不影响测试结论。
结论:
cpu占用率:
graalVm21 > openjdk11 > openjdk15
堆内存使用情况
graalVm21 < openjdk15 ~ openjdk11
开始测试时出现大于200ms请求的次数(笔者所在项目关注的指标)
openjdk11 > graalvm21 > openjdk15
不定时GC JVM参数下,两次GC间隔:
openjdk15 > graalvm21 > openjdk11
2000并发下tps 为:
openjdk15 > graalvm21 ~ openjdk11
综合比较: graalVm21 和 openjdk15 ZGC内存性能相关相差不大,但是openjdk15开始压测时出现大于200ms请求量较少(即TTSP短),tps相对较高。
在本次测试中,非定时gc情况下TPS小于定时gc情况下tps
两种JVM参数下GC停顿时间99线为:
定时GC:0.845ms
非定时GC : 0.273ms
均不超过1ms
扩展:
经过本次测试,不禁产生两个疑问:
1.openjdk15 的zgc相较于11做了哪些优化
2.为什么定时gc下TPS比非定时gc下TPS高
问题1:经过一系列的测试和资料查找发现jdk11-15 zgc的相关优化如下(红框内为涉及本次测试优化):
1.zgc在jdk13时缩短了到达safepoint的时间(ttsp)
2.jdk14引入了安全点可识别数组分配(将初始化为0期间的数组标记为不可见根,任何大小的数组都不会影响TTSP)(不在截图内,在jdk14官方博客中可以找到相关优化介绍)
3.jdk12支持并发卸载类,并且进一步缩短了停顿时间
问题2:经过对gc日志的分析得出:
ZGC初次标记和搬运阶段定时GC平均时间小于非定时GC平均时间,因为定时GC初次标记阶段需要标记的root相对较少,搬运对象也相对较少。
所以其stw相对较小。即TPS相对较高。
思考:
本次测试并不代表所有情况下定时gc都比非定时gc更优。因为非定时gc情况下,jvm会根据配置的系数来进行GC,当流量突然增大,会增加gc次数,频繁gc,当流量平稳时,会减少gc次数。是不同的GC策略,实际生产应用中还需要根据不同场景进行选择。