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

news2025/2/23 23:26:02

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

1.png

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.png

2.3 GC工作过程

与CMS中的ParNew和G1类似,ZGC也采用标记-复制算法,不过ZGC通过着色指针和读屏障技术,解决了转移过程中准确访问对象的问题,在标记、转移和重定位阶段几乎都是并发执行的,这是ZGC实现停顿时间小于10ms目标的最关键原因。

3.png

从上图中可以看出,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.png

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>

5.png

  • 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后启动报错

6.png

在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

13.png

具体每个参数的含义,这里不做介绍,可参考官网文档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 压测结果

直接上图

7.png

8.png

9.png

正如 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。

10.png

  • 内存分配阻塞:这一般是因为垃圾生产速度大于回收速度,垃圾来不及回收,垃圾将堆占满时,线程会阻塞等待GC完成,关键字是Allocation Stall(被阻塞的线程名称)

11.jpeg

如果出现此类日志,可以尝试如下方法解决:

  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大页内存后会提升应用的性能。

12.png

开启方式见官网文档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是一个明智的选择。

文: 新一

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

未经得物技术许可严禁转载,否则依法追究法律责任!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/652991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nodejs六、数据库操作

零、文章目录 Nodejs六、数据库操作 1、MYSQL数据库 MYSQL相关知识请参考MYSQL基础 2、在项目中操作 MySQL &#xff08;1&#xff09;操作数据库的步骤 安装操作 MySQL 数据库的第三方模块&#xff08;mysql&#xff09;通过 mysql 模块连接到 MySQL 数据库通过 mysql 模…

chatgpt赋能python:Python编写网站的SEO指南

Python 编写网站的 SEO 指南 Python 是一个高可扩展性和灵活性的编程语言&#xff0c;在创建面向 Web 的应用程序和网站时非常强大。但是&#xff0c;即使你创建了一个出色的网站&#xff0c;也需要将它放在正确的地方以便被人们发现。 搜索引擎优化&#xff08;SEO&#xff0…

【深度学习】2-2 神经网络 - 前向传播实现3层神经网络

神经网络分层 神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数。 用图来表示神经网络的话&#xff0c;把最左边的一列称为输入层&#xff0c;最右边的一列称为输出层&#xff0c;中间的一列称为中间层。中间层有时也叫隐藏层&#xff08;或隐含层&#xf…

深入了解计算机SNMP协议:原理、功能和应用场景

前言 简单网络管理协议&#xff08;SNMP&#xff09;是一种用于管理网络设备的协议&#xff0c;它可以让管理员通过网络对设备进行监控、配置和故障排除等操作。本文将详细介绍SNMP的版本、管理信息库MIB、管理信息结构&#xff08;SMI&#xff09;、SNMP报文、5种协议数据单元…

avive零头撸矿

Avive 是一个透明的、自下而上替代自上而下的多元网络&#xff0c;旨在克服当前生态系统的局限性&#xff0c;实现去中心化社会。 aVive&#xff1a;一个基于 SBT 和市场的 deSoc&#xff0c;它使 dapps 能够与分散的位置 oracle 和 SBT 关系进行互操作。您的主权社交网络元宇宙…

Vue中如何进行表单验证码与滑动验证?

Vue中如何进行表单验证码与滑动验证&#xff1f; 在Web应用程序中&#xff0c;表单验证码和滑动验证是常见的安全机制&#xff0c;用于防止恶意攻击和机器人攻击。在Vue中&#xff0c;我们可以使用许多不同的库来实现这些功能。本文将介绍如何使用Vue和vue-verify-code库来实现…

docker中部署lnmp架构

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 docker中部署lnmp架构 前言一、安装docker和docker-compose二、文件部署准备三、创建ngixn虚拟主机配置文件四、创建html文件夹五、启动容器文件结构 前言 Docker是一种轻量…

5.6.1 Ext JS之标签页的关闭和批量关闭

Tab Panel 是包含多个标签页的面板, 这是一种很常用的组件, 类似于浏览器的标签页。关于 Ext JS的Tab Panel的基本使用可以参考: [Ext JS3.9] 标签面板(TabPanel )介绍与开发, 本篇介绍如何关闭单个标签页和批量关闭标签页。 Tab 标签页的可关闭 默认状况下,标签页是无…

23.反射(reflection)|Java学习笔记

文章目录 反射机制Java反射机制原理图Java反射机制可以完成反射相关的主要类反射优点和缺点 Class类 反射机制 一个简单的例子&#xff1a; package com.edu.reflection.question;import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.Invo…

消防安全知识答题活动小程序v5.0-支持答题后抽奖

关于答题抽奖活动小程序的设计思考 1. 功能设计&#xff1a;作为答题抽奖活动小程序&#xff0c;核心功能应包括答题和抽奖两部分。用户通过答题获取抽奖机会&#xff0c;答题可以设置为多个题目&#xff0c;用户回答正确则获得相应分数。在用户答完问题后&#xff0c;可以立即…

优思学院|企业业绩差的7大原因,善用精益管理可解决

在当今竞争激烈的商业环境中&#xff0c;一些企业的业绩表现出了较差的趋势&#xff0c;这可能是由于多种原因造成的。下面将探讨企业业绩差的七大原因&#xff0c;并介绍如何善用精益管理来提升企业的绩效。 1. 战略定位不清 企业业绩差的一个常见原因是战略定位不清。如果企…

如何用流量涡轮打造属于自己的汽车行业高价值私域流量池

01. 私域提升品牌价值 2010年以来&#xff0c;中国汽车工业就一直处于两位数的增长。这使得国内外品牌都能在市场上站稳脚跟。这为许多汽车公司提供了获得可观利润的绝佳机会。汽车成为明星行业&#xff0c;在此阶段的车企高管们也成为了行业翘楚。然而&#xff0c;2018年之后…

延迟渲染G-buffer所占显存带宽计算(解决移动端和抗锯齿的若干疑问)

延迟渲染需要在前面阶段&#xff0c;将计算的内容保留在N张G-buffer中&#xff0c;但是网上的文章只是提及了G-buffer应该压缩&#xff0c;并且尽量少用&#xff0c;没有说明G-buffer所占带宽应该是多少&#xff0c;我将在下面介绍G-buffer所占显存带宽的详细计算方法 G-buffe…

4、DuiLib了解 XML使用和布局控制

文章目录 1、了解 XML使用和布局控制2、内外边距3、浮动4、占位符5、默认样式6、全局字体 1、了解 XML使用和布局控制 通过上一篇的学习我们可以制作一个简单的布局了&#xff0c;但是没有控件的窗口做再好的布局有什么用呀。赶紧找些素材&#xff0c;我们来做一个标准的 Wind…

黄金期货交易规则有哪些?黄金期货交易规则详解

黄金期货交易是一种高风险的投资工具&#xff0c;因此新手投资者在准备交易前建议先学习重要的黄金期货交易规则&#xff0c;对黄金期货产品交易有一个大概的了解。黄金期货交易规则有哪些&#xff1f;以下是重要的黄金期货交易规则详解 黄金期货交易规则一、交易前需要先开户 …

在Deepin虚机中共享使用主机文件夹

一、系统环境&#xff1a; 操作系统&#xff1a;Win11 虚机版本&#xff1a;VMWare workstation 16 pro 虚机系统&#xff1a;deepin 20.9 二、主机中操作 VMWare Workstation/虚拟机/设置/选项/共享文件夹 默认为已禁用&#xff0c;在右侧选择“总是启用”&#xff0c;在…

如何知道自己的论文适合哪个期刊呢?

论文写得好&#xff0c;不如期刊选得好&#xff01; 如何才能快速、轻松地找到适合的SCI论文期刊呢&#xff1f;在这篇文章中&#xff0c;笔者将分享选择SCI期刊的方法&#xff0c;并总结了需要考虑的要素&#xff0c;可以帮助作者缩小期刊选择的范围&#xff0c;迅速做出最佳决…

自制开源的 Midjourney、Stable Diffusion “咒语”作图工具

本篇文章聊聊如何通过 Docker 和八十行左右的 Python 代码&#xff0c;实现一款类似 Midjourney 官方图片解析功能 Describe 的 Prompt 工具。 让你在玩 Midjourney、Stable Diffusion 这类模型时&#xff0c;不再为生成 Prompt 描述挠头。 写在前面 本文将提供两个版本的工…

0基础学习VR全景平台篇第44篇:编辑器底部菜单- 添加多态

大家好&#xff0c;欢迎观看蛙色VR官方系列——后台使用课程&#xff01; 本期为大家带来蛙色VR平台&#xff0c;底部菜单—添加多态功能操作。 功能位置示意 一、本功能将用在哪里&#xff1f; 多态场景&#xff0c;指同一个空间场景不同状态下的无缝切换&#xff0c;通过拍…

netty 总结

1. NIO和BIO的比较 BIO以流的方式处理数据&#xff0c;而NIO以块的方式处理数据&#xff0c;块I/O的效率比流I/O高很多。 BIO是阻塞的&#xff0c;NIO是非阻塞的 BIO基于字节流和字符流进行操作&#xff0c;而NIO是基于Channel&#xff08;通道&#xff09;和Buffer&#xff08…