Redian新闻
>
亚毫秒GC暂停到底有多香?JDK17+ZGC初体验

亚毫秒GC暂停到底有多香?JDK17+ZGC初体验

公众号新闻

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%(低延时换吞吐量很值,吞吐量扩容即可解决)


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的一种处理动作,用于复制对象的收集器阶段)因为复制大对象的代价非常高。


2.3 GC工作过程

与CMS中的ParNew和G1类似,ZGC也采用标记-复制算法,不过ZGC通过着色指针和读屏障技术,解决了转移过程中准确访问对象的问题,在标记、转移和重定位阶段几乎都是并发执行的,这是ZGC实现停顿时间小于10ms目标的最关键原因。
从上图中可以看出,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,下载地址:https://jdk.java.net/archive/,选择版本17 GA


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版本升级https://projectlombok.org/changelog
<dependency>    <groupId>org.projectlombok</groupId>    <artifactId>lombok</artifactId>   <!-- <version>1.16.20</version>-->    <version>1.18.22</version></dependency>

  • 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后启动报错
在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参数
ZGC特有参数
ZGC 诊断选型
-XX:MinHeapSize, -Xms
-XX:InitialHeapSize, -Xms
-XX:MaxHeapSize, -Xmx
-XX:SoftMaxHeapSize
-XX:ConcGCThreads
-XX:ParallelGCThreads
-XX:UseDynamicNumberOfGCThreads
-XX:UseLargePages
-XX:UseTransparentHugePages
-XX:UseNUMA
-XX:SoftRefLRUPolicyMSPerMB
-XX:AllocateHeapAt
-XX:ZAllocationSpikeTolerance
-XX:ZCollectionInterval
-XX:ZFragmentationLimit
-XX:ZMarkStackSpaceLimit
-XX:ZProactive
-XX:ZUncommit
-XX:ZUncommitDelay
-XX:ZStatisticsInterval
-XX:ZVerifyForwarding
-XX:ZVerifyMarking
-XX:ZVerifyObjects
-XX:ZVerifyRoots
-XX:ZVerifyViews
注意:使用以上参数需要开启-XX:+UnlockDiagnosticVMOptions
具体每个参数的含义,这里不做介绍,可参考官网文档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

压测结果

直接上图
正如 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日志配置可以参考:https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#enable-logging-with-the-jvm-unified-logging-framework


6.2 STW关键日志

其中我们重点关注的就是GC的STW情况,以下是一些关键字代表GC STW阶段
  • 最基本的STW三阶段,初始标记:日志中Pause Mark Start,再标记:日志中Pause Mark End,初始转移:日志中Pause Relocate Start。
  • 内存分配阻塞:这一般是因为垃圾生产速度大于回收速度,垃圾来不及回收,垃圾将堆占满时,线程会阻塞等待GC完成,关键字是Allocation Stall(被阻塞的线程名称)

如果出现此类日志,可以尝试如下方法解决:
    • -XX:ZCollectionInterval 该配置含义:两个 GC 周期之间的最大间隔(单位秒)。默认情况下,此选项设置为 0(禁用),可以适当调小该配置,让GC周期缩短、提升垃圾回收速度,但这会提升应用CPU占用。
    • -XX:ZAllocationSpikeTolerance官方的解释是 ZGC 的分配尖峰容忍度。其实就是数值越大,越早触发回收。可以适当调大该配置,更早触发回收,提升垃圾回收速度,但这会提升应用CPU占用。
  • 安全点:所有线程进入到安全点后才能进行GC,ZGC定期进入安全点判断是否需要GC。先进入安全点的线程需要等待后进入安全点的线程直到所有线程挂起。日志关键字safepoint ... stopped
  • dump线程、内存:比如jstack、jmap命令,一般是手动dump导致,日志关键字HeapDumper

7

Linux大页内存

在openjdk的官网上也能看到,开启Linux大页内存后会提升应用的性能。
开启方式见官网文档https://wiki.openjdk.org/display/zgc/Main#Main-EnablingLargePagesOnLinux,注意除了修改系统配置外,还需要在进程JVM启动参数中新增-XX:+UseLargePages配置
经过几轮压测实际测试下来,发现在开启Linux大页后,CPU有8%左右的下降,但是由于大页面会提前预留指定大小的内存,会导致机器的内存使用率较高。而且目前生产环境没有其他应用开启此配置,稳定性有待考究,生产环境自行评估是否开启。

8

总结

在本篇文章中,我们探讨了如何升级到JDK 17,并使用最新一代垃圾回收器ZGC。经过实践和测试,我们发现升级后的系统在垃圾回收方面表现出色,暂停时间被有效控制在1毫秒内。尽管这一优化过程可能会消耗额外的CPU资源,但所获得的超低GC暂停时间显然是非常值得的。总之,相比其他垃圾回收器,ZGC 的性能和稳定性已经非常优秀,而且不需要太多的调优。在大多数情况下,使用 ZGC官方推荐的默认设置即可获得优秀的性能表现。对于那些RT敏感型应用,升级到JDK 17并采用ZGC是一个明智的选择。
*文/ 新一


近期有较多投资机构在寻找优质开源创业项目,有意向可以添加微信 Hikalin,获取一手信息。


END



马斯克承认“家丑”,去年大裁员给自己挖了坑



这里有最新开源资讯、软件更新、技术干货等内容

点这里 ↓↓↓ 记得 关注✔ 标星⭐ 哦


微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
比JDK最高快170倍,蚂蚁集团开源高性能多语言序列化框架Fury比 JDK 最高快 170 倍,蚂蚁集团开源高性能多语言序列化框架 Fury重磅!Spring 6.1 M4发布,已兼容虚拟线程和JDK 21冷泡出香,年轻人追捧的“鸭屎香”究竟有多香?何超欣:直言看不上王思聪?撮合姚安娜何猷君,凭啥独得赌王宠爱收入稅减少20%!加拿大这个唯一没有消费税的省有多香?“如果UFO停到了你家门口...”快被网友的评论笑死了哈哈哈[摄影] 坚实的力量--索尼70-200GM二代初体验Spring 6.1 M4发布,已兼容虚拟线程和JDK 21【揭秘】加拿大紧缺职业个人护理PSW有多香?风靡湾区的“剩菜盲盒”,到底有多香?闷声干大事!腾讯捐了个JDK!JDK 21中的结构化并发:并发编程的一次飞跃Java近期新闻:JDK 21的JEP、Spring Cloud AWS 3.0以及OptaPlanner转移至Timefold为 Java 开疆扩土的 ZGC“宅家美容院”有多香?佛系护肤也能轻松get高效抗老反季买冲锋衣有多香?比T恤还便宜!防水、防风、防污三合一,下暴雨都不怕!反季买羽绒服有多香?原价600+的黑金羽绒服,破价低至119元,短款长款、马甲都有,还有儿童款!医学博士应聘做校医,校医到底有多香?图们江出海口JDK 21从主线fork ,成为下一个Java LTS版本Java近期新闻:JDK 21进入Rampdown阶段、JEP 404、JDK 22专家组、Jakarta EE 11升级[摄影] 计划外购买徕卡M10初体验和XJJ人像主动争取的爱情有多香?美国博士有多香?如何合理规划申请时间?汤姆●琼斯—一个弃儿的个人史01.04A(重译)放心,AIGC暂时替代不了你蔬菜香饭做对了就是一道诱人的国王盛宴——Veg Biryani承载着素食爱好者的美食梦想【视频】【现货】GSC初音5周年交响乐手办吃现啦!Java ZGC 垃圾收集器全面增强24Fall请注意!耶鲁、MIT开设新专业,美国本都爱的跨学科到底有多香?直播间对话Chris女士,150天取得工卡+回美证究竟有多香?6月14日晚9点见近作三首如何高效定位故障?这款 K8s 日志查看神器有多香?能连接三大热门移民项目,加拿大紧缺职业个人护理PSW有多香?
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。