HiSEN

Java HotSpot 虚拟机 CMS、G1 参数优化对比记录

零、背景说明

目前测试机器为 4C8G
两台机器完全处理一样的工作
大部分时间对象朝生夕死,很少进入老年代
CMS 指定了新生代最大 1536M,略微有点浪费
于是设置 G1 自动调节各个区域大小,看看能否有所改善
也因为最近重温了两本 JVM 相关的书籍,找机会进行实践看看

一、G1 设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-Xms4096M
-Xmx4096M
-XX:+UseG1GC
-XX:+UnlockDiagnosticVMOptions
-XX:SurvivorRatio=8
-XX:+ParallelRefProcEnabled
-XX:MaxTenuringThreshold=6
-XX:ParGCCardsPerStrideChunk=4096
-XX:MaxGCPauseMillis=100
-XX:MaxMetaspaceSize=256M
-XX:MetaspaceSize=256M
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-Xloggc:/home/hisen/gc.log

二、CMS 设置

最大最小设置成一致的值,是为了不让堆大小进行收缩(缩的过程会 GC)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 初始堆大小
-Xms4096M
# 最大堆大小
-Xmx4096M
# 年轻代的大小
-Xmn1536M
# eden 区比例,此处为 8:1:1
-XX:SurvivorRatio=8
# 最大元空间大小
-XX:MaxMetaspaceSize=256M
# 最小元空间大小
-XX:MetaspaceSize=256M
# 使用 CMS 垃圾收集器
-XX:+UseConcMarkSweepGC
# 只使用占用率作为启动 CMS GC 的标准,配合 CMSInitiatingOccupancyFraction 使用
-XX:+UseCMSInitiatingOccupancyOnly
# CMS垃圾收集器,当老年代达一定比例,触发CMS垃圾回收。此处为 70%
-XX:CMSInitiatingOccupancyFraction=70
# 默认为 false,并行的处理 Reference 对象,如 WeakReference,除非在 GC log 里出现 Reference 处理时间较长的日志,否则效果不会很明显。
-XX:+ParallelRefProcEnabled
# 开启或关闭在CMS重新标记阶段之前的 YGC 尝试
-XX:+CMSScavengeBeforeRemark
# 晋升老年代的阈值
-XX:MaxTenuringThreshold=6
# 解锁诊断参数,否则 ParGCCardsPerStrideChunk 不生效
-XX:+UnlockDiagnosticVMOptions
# 每个线程扫描 old gen 的 CardTable (一定大小的内存块) 个数
-XX:ParGCCardsPerStrideChunk=4096
# 打印 GC 的时间戳
-XX:+PrintGCDateStamps
# 打印 GC 明细日志
-XX:+PrintGCDetails
# 存放 GC 日志的路径
-Xloggc:/home/hisen/gc.log

三、结果对比

TypeDurationXmnYGC countYGC avgInterval Time
G172 hrs 21 min 8 sec1.66G759619.6 ms34 sec 294 ms
CMS70 hrs 24 min 51 sec1.5G702914.4 ms36 sec 68 ms

目前应用堆响应时间比较敏感,追求低延迟。
就上表来看,G1 新生代的大小确实上去了,但并不尽如人意。
但是随着而来的是 G1 『更频繁的YGC』以及『更长的停顿时间』
在《深入理解JVM虚拟机》第三版看到,
由于 G1 处理跨 region 需要耗费额外 10% ~ 20% 的资源,小堆 (堆大小<8G) 上没有优势。

JVM 新生代大小与老年代大小默认为 1:2,
大部分应用按这个设置是 OK 的,
虽然有时候看着老年代一直很小,
设置那么大浪费内存,但是当调用量大的时候,就能派上用场,减少 Full GC。

先占个坑,后续继续填内容。