百日筑基第五十二天-初识ZGC
前言
垃圾回收器的暂停问题一直是Java工程师关注的重点,特别是对实时响应要求较高的服务来说,CMS和G1等主流垃圾回收器的数十毫秒乃至上百毫秒的暂停时间相当致命。此外,调优门槛也相对较高,需要对垃圾回收器的内部机制有一定的了解,才能够进行有效的调优。 为了解决此类问题,JDK 11开始推出了一种低延迟垃圾回收器ZGC。ZGC使用了一些新技术和优化算法,可以将GC暂停时间控制在10毫秒以内,而在JDK 17的加持下,ZGC的暂停时间甚至可以控制在亚毫秒级别!
设计目标
ZGC 最初在 JDK 11 中作为实验性功能引入,并在 JDK 15 中宣布为生产就绪。作为一款低延迟垃圾收集器,旨在满足以下目标:
- 8MB到16TB的堆大小支持
- 10ms最大GC暂时
- 最糟糕的情况下吞吐量会降低15%(低延时换吞吐量很值,吞吐量扩容即可解决)
ZGC 内存分布
ZGC与传统的CMS、G1不同、它没有分代的概念,只有类似G1的Region概率,ZGC 的 Region可以具有如下图所示的大中下三类容量:
- 小型 Region(Small Region):容量固定为2MB,用于放置小于 256KB的小对象。
- 中型 Region(Medium Region):容量固定为 32MB,用于放置大于 256KB但是小于 4MB的对象。
- 大型 Region(Large Region):容量不固定,可以动态变化,但必须为 2MB的整数倍,用于放置 4MB或以上的大对象。每个大型 Region中会存放一个大对象,这也预示着虽然名字叫“大型 Region”,但它的实际容量完全有可能小于中型Region,最小容量可低至4MB。大型 Region在ZGC的实现中是不会被重分配的(重分配是ZGC的一种处理动作,用于复制对象的收集器阶段)因为复制大对象的代价非常高。
GC工作过程
与CMS中的ParNew和G1类似,ZGC也采用标记-复制算法,不过ZGC通过着色指针和读屏障技术,解决了转移过程中准确访问对象的问题,在标记、转移和重定位阶段几乎都是并发执行的,这是ZGC实现停顿时间小于10ms目标的最关键原因。
VM参数替换
下面是一些通用GC参数和ZGC特有参数以及ZGC的一些诊断选型,来自官网:Main - Main - OpenJDK Wiki
JKD8+G1的启动参数:
-server -Xms36600m -Xmx36600m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:+PrintReferenceGC
-XX:+ParallelRefProcEnabled
-XX:G1HeapRegionSize=16m
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/apps/errorDump.hprof
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintHeapAtGC
-XX:+PrintGCApplicationConcurrentTime
-verbose:gc
-Xloggc:/opt/apps/logs/${app_name}-gc.log
JDK17+ZGC的启动参数如下:
-server -Xms36600m -Xmx36600m
#开启ZGC
-XX:+UseZGC
#GC周期之间的最大间隔(单位秒)
-XX:ZCollectionInterval=120
#官方的解释是 ZGC 的分配尖峰容忍度,数值越大越早触发GC
-XX:ZAllocationSpikeTolerance=4
#关闭主动GC周期,在主动回收模式下,ZGC 会在系统空闲时自动执行垃圾回收,以减少垃圾回收在应用程序忙碌时所造成的影响。如果未指定此参数(默认情况),ZGC 会在需要时(即堆内存不足以满足分配请求时)执行垃圾回收。
-XX:-ZProactive
#GC日志
-Xlog:safepoint=trace,classhisto*=trace,age*=info,gc*=info:file=/opt/logs/gc-%t.log:time,level,tid,tags:filesize=50M
#发生OOM时dump内存日志
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/apps/errorDump.hprof
压测结果
直接上图
正如 ZGC 设计目标所描述,它将 GC 暂停时间从过去的几十毫秒降低到了令人惊叹的亚毫秒级别。然而,这种超低延迟表现也需要一定的代价,因为在实现低延迟的同时,ZGC 会占用一定的 CPU 资源。通常情况下,ZGC 占用的 CPU 比例不会超过 15%。在彩虹桥项目中,使用以上推荐的 JVM 参数后,ZGC 占用的 CPU 资源为 6% 左右。