1 前语

废物收回器的暂停问题一直是Java工程师关注的要点,特别是对实时呼应要求较高的服务来说,CMS和G1等干流废物收回器的数十毫秒乃至上百毫秒的暂停时刻恰当致命。此外,调优门槛也相对较高,需求对废物收回器的内部机制有必定的了解,才能够进行有用的调优。 为了处理此类问题,JDK 11开端推出了一种低推迟废物收回器ZGC。ZGC运用了一些新技能和优化算法,能够将GC暂停时刻控制在10毫秒以内,而在JDK 17的加持下,ZGC的暂停时刻乃至能够控制在亚毫秒等级!

2 ZGC

ZGC相关介绍、原理,网上现已有许多相似文章,这儿只做简略介绍。

2.1 规划方针

ZGC 开始在 JDK 11 中作为试验性功能引进,并在 JDK 15 中宣布为出产就绪。作为一款低推迟废物收集器,旨在满足以下方针:

  • 8MB到16TB的堆巨细支持
  • 10ms最大GC暂时
  • 最糟糕的状况下吞吐量会下降15%(低延时换吞吐量很值,吞吐量扩容即可处理)

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

2.2 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暂停到底有多香?JDK17+ZGC初体验|得物技术

2.3 GC作业进程

与CMS中的ParNew和G1相似,ZGC也选用符号-仿制算法,不过ZGC经过着色指针和读屏障技能,处理了搬运进程中准确访问目标的问题,在符号、搬运和重定位阶段几乎都是并发履行的,这是ZGC完结中止时刻小于10ms方针的最关键原因。

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

从上图中能够看出,ZGC只要三个STW阶段:初始符号,再符号,初始搬运。 具体搬运进程,网上有很多相似文章,这儿不做具体介绍,大家有爱好能够参考以下文章:

新一代废物收回器ZGC的探究与实践 ZGC 最新一代废物收回器 | 程序员进阶

3 为什么挑选JDK17呢?

JDK 17于9月14日发布,是一个长时刻支持(LTS)版别,这意味着它将在许多年内得到支持和更新。这也是第一个LTS版别,其间包含了一个可用于出产环境的ZGC版别。回忆一下,ZGC的试验版别现已包含在JDK 11(之前的LTS版别)中,而第一个可用于出产环境的ZGC版别呈现在JDK 15(一个非LTS版别)中。

4 晋级进程

从JDK8+G1晋级到JDK17+ZGC,主要是在代码层面和JVM发动参数层面的做适配。

4.1 JDK下载

首先jdk17挑选的是openjdk,下载地址:jdk.java.net/archive/,挑选… GA

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

4.2 代码适配

  • JDK11移除了 Java EE and CORBA 的模块

项目中假如用到javax.annotation.*、javax.xml.*等等开头的包,需求手动引进对应依靠

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-core</artifactId>
</dependency>
<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-impl</artifactId>
</dependency>
  • maven相关依靠版别晋级
<!-- 仅供参考 -->
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
<maven-deploy-plugin.version>3.0.0-M1</maven-deploy-plugin.version>
<maven-release-plugin.version>3.0.0-M1</maven-release-plugin.version>
<maven-site-plugin.version>3.9.1</maven-site-plugin.version>
<maven-enforcer-plugin.version>3.0.0-M2</maven-enforcer-plugin.version>
<maven-project-info-reports-plugin.version>3.1.0</maven-project-info-reports-plugin.version>
<maven-plugin-plugin.version>3.6.1</maven-plugin-plugin.version>
<maven-javadoc-plugin.version>3.3.0</maven-javadoc-plugin.version>
<maven-source-plugin.version>3.2.1</maven-source-plugin.version>
<maven-jxr-plugin.version>3.0.0</maven-jxr-plugin.version>
  • Lombok版别晋级projectlombok.org/changelog
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
   <!-- <version>1.16.20</version>-->
    <version>1.18.22</version>
</dependency>

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

  • Java9 模块化后,不允许运用程序查看来自JDK的一切类,会影响部分反射的运转,需求经过以下指令处理
--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
  • 本地运用了transmittable-thread-local-2.14.2.jar后发动报错

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

在agent后边加上日志输出即可处理,至于原因,猜测是跟类加载顺序有联系

-javaagent:/Users/admin/Documents/transmittable-thread-local-2.14.2.jar
=ttl.agent.logger:STDOUT

以上内容仅针对彩虹桥项目晋级遇到的问题,不同的业务代码适配的状况或许不一样,需求依据实践状况寻找处理方案。

4.3 JVM参数替换

下面是一些通用GC参数和ZGC特有参数以及ZGC的一些诊断选型,来自官网:Main – Main – OpenJDK Wiki

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

具体每个参数的意义,这儿不做介绍,可参考官网文档The java Command,里边有具体阐明。

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

5 压测成果

直接上图

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

正如 ZGC 规划方针所描绘,它将 GC 暂停时刻从曩昔的几十毫秒下降到了令人惊叹的亚毫秒等级。然而,这种超低推迟体现也需求必定的代价,因为在完结低推迟的一起,ZGC 会占用必定的 CPU 资源。通常状况下,ZGC 占用的 CPU 份额不会超过 15%。在彩虹桥项目中,运用以上推荐的 JVM 参数后,ZGC 占用的 CPU 资源为 6% 左右。

6 ZGC日志

6.1 输出ZGC日志

GC日志中包含有关 GC 操作的具体信息,能够帮咱们分析当时GC存在的问题。先来看一下上面JVM参数中关于GC日志的参数

-Xlog:safepoint=trace,classhisto*=trace,age*=info,gc*=info:file=/opt/logs/gc-%t.log:time,level,tid,tags:filesize=50M
  • safepoint=trace:记载关于 safepoint 的 trace 等级日志。 Safepoint 是 JVM 中一个特殊的状态,它用于确保一切线程在特定操作(如废物收回、代码优化等)之前进入安全状态。
  • classhisto*=trace:记载与类的前史相关的 trace 等级日志。 age*=info:记载与目标年龄(在新生代中存在的时刻)相关的 info 等级日志。
  • gc*=info:记载与废物收回相关的 info 等级日志。
  • file=/opt/logs/gc-%t.log:将日志写入到 /opt/logs/ 目录下的文件中,文件名为 gc-%t.log,其间 %t 是一个占位符,表示当时时刻戳。
  • time,level,tid,tags:在每个日志记载中包含时刻戳、日志等级、线程 ID 和标签。
  • filesize=50M:设置日志文件的巨细约束为 50MB。当日志文件巨细达到此约束时,JVM 将创立一个新的日志文件并继续记载。

更具体的gc日志装备能够参考:docs.oracle.com/en/java/jav…

6.2 STW关键日志

其间咱们要点关注的便是GC的STW状况,以下是一些关键字代表GC STW阶段

  • 最基本的STW三阶段,初始符号:日志中Pause Mark Start,再符号:日志中Pause Mark End,初始搬运:日志中Pause Relocate Start。

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

  • 内存分配堵塞:这一般是因为废物出产速度大于收回速度,废物来不及收回,废物将堆占满时,线程会堵塞等候GC完结,关键字是Allocation Stall(被堵塞的线程称号)

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

假如呈现此类日志,能够尝试如下方法处理:

  1. -XX:ZCollectionInterval 该装备意义:两个 GC 周期之间的最大距离(单位秒)。默许状况下,此选项设置为 0(禁用),能够恰当调小该装备,让GC周期缩短、提高废物收回速度,但这会提高运用CPU占用。
  2. -XX:ZAllocationSpikeTolerance官方的解释是 ZGC 的分配尖峰容忍度。其实便是数值越大,越早触发收回。能够恰当调大该装备,更早触发收回,提高废物收回速度,但这会提高运用CPU占用。
  • 安全点:一切线程进入到安全点后才能进行GC,ZGC定期进入安全点判断是否需求GC。先进入安全点的线程需求等候后进入安全点的线程直到一切线程挂起。日志关键字safepoint … stopped
  • dump线程、内存:比如jstack、jmap指令,一般是手动dump导致,日志关键字HeapDumper

7 Linux大页内存

在openjdk的官网上也能看到,敞开Linux大页内存后会提高运用的功能。

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术

敞开方法见官网文档wiki.openjdk.org/display/zgc…

经过几轮压测实践测试下来,发现在敞开Linux大页后,CPU有8%左右的下降,可是因为大页面会提早预留指定巨细的内存,会导致机器的内存运用率较高。并且目前出产环境没有其他运用敞开此装备,稳定性有待考究,出产环境自行评估是否敞开。

8 总结

在本篇文章中,咱们探讨了怎么晋级到JDK 17,并运用最新一代废物收回器ZGC。经过实践和测试,咱们发现晋级后的体系在废物收回方面体现出色,暂停时刻被有用控制在1毫秒内。虽然这一优化进程或许会耗费额外的CPU资源,但所获得的超低GC暂停时刻显然是十分值得的。总归,相比其他废物收回器,ZGC 的功能和稳定性现已十分优异,并且不需求太多的调优。在大多数状况下,运用 ZGC官方推荐的默许设置即可获得优异的功能体现。对于那些RT灵敏型运用,晋级到JDK 17并选用ZGC是一个明智的挑选。

文: 新一

本文属得物技能原创,来源于:得物技能官网

未经得物技能许可严禁转载,不然依法追究法律责任!