首页 > 其他分享 >你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队

时间:2024-01-10 12:31:46浏览次数:31  
标签:并发 JDK17 对象 调优 GC 内存 线程 ZGC

1、前言

不要犹豫了,GC最大停顿时间小于1ms,支持16TB内存,这么高的性能提升,也不需要复杂的调优,节省了这个时间,你去陪对象不香嘛。

上篇文章给大家带来了JDK11升级JDK17的最全实践,相信大家阅读后对于升级JDK17有了基本的了解。同时我们也会比较好奇,ZGC的原理是啥样的,怎么做到停顿时间那么短? 本文将通过对比ZGC与传统垃圾回收器的改动点,从多个维度综合分析为什么ZGC的停顿时间那么短。同时由于ZGC的深层次原理可能较为晦涩难懂,本文将尽可能采用图文并茂的方式,以使大家更容易理解ZGC的核心原理。

2、ZGC是什么

ZGC垃圾收集器( Z Garbage Collector )是一种可伸缩的低延迟垃圾收集器,ZGC 可以很好地处理从几百兆字节到16TB堆大小空间的垃圾回收,而中断应用程序线程的时间不超过1ms。特别适合需要低延迟的应用,同时暂停时间与正在使用的堆大小无关。。

ZGC 最初作为 JDK 11 中的实验性功能引入,并在 JDK 15 中宣布生产就绪。

ZGC的技术特点:

•并发

•基于分区的,不再是传统的分代模型,JDK21开始支持分代

•自动整理内存

•支持NUMA,Non-Uniform Memory Access(非一致内存访问)

•染色指针,将GC标识信息存储在指针上的技术

•读屏障,读取的时候去更新对象的指针引用关系

ZGC的目标:

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_G1

3、有了G1,为什么还要引入ZGC

G1垃圾收集器采用了部分区域回收的处理方式,有效解决了传统垃圾收集器中全堆扫描所带来的性能问题,极大地改善了在堆内存较大情况下的停顿时间。然而,随着硬件性能的不断提升,G1回收器也面临着极大的性能限制。尽管G1经过多个版本的优化和调优,已经接近极限,但仍然无法满足日益增长的机器内存需求。

说到底,G1的性能还是不能满足现阶段的硬件配置,G1的GC停顿时间相对较长,比如我上篇文章中的压测报告中,G1的最大停顿时间达到了610ms

4、ZGC为什么那么快?

4.1、分代模型和分区模型

传统的垃圾回收器都采用分代的垃圾回收模型。新一代ZGC采用分区模型(类似于G1),分为三种类型的分区(2MB、32MB、N*2MB),从JDK21开始支持分代模型

低延迟:

ZGC的分页模型允许并发地处理内存分配和回收操作,从而减少了垃圾收集的停顿时间。相比之下,分代模型需要在不同代之间进行对象的复制或移动,可能会导致更长的停顿时间。

内存利用率高:

ZGC的分页模型可以动态地调整页的大小,以适应不同大小的对象。这样可以提高内存的利用率,减少内存碎片的产生。而分代模型中,不同代的内存空间是固定的,可能会导致内存碎片的问题。

可伸缩性:

ZGC的分页模型允许将堆内存划分为多个页区,并且每个页区都有独立的垃圾收集线程。这样可以实现垃圾收集的并行性,提高系统的可伸缩性和吞吐量。而分代模型中,不同代的垃圾收集是串行或并发-串行的,可能无法充分利用多核处理器的性能。

适应大内存堆:

ZGC的分页模型可以有效地管理大内存堆。它可以根据需要动态地增加或减少页的数量,以适应大内存堆的需求。而分代模型中,不同代的内存空间是固定的,无法有效地管理大内存堆。

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_G1_02

4.2、GC标记信息位置的变化

传统垃圾回收器通过扫描堆中的对象(扫描堆空间是很慢的),根据对象头中的可达性标记信息,来确定对象是否应该被回收。

ZGC不直接依赖于对象头中的信息来进行垃圾回收决策,而是把GC信息存在内存引用地址上。GC时通过扫描栈上的内存引用指针来确定对象的引用关系和可达性,从而来判断对象是否应该被回收。

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_G1_03

4.3、引用指针的变化-指针着色

ZGC通过64位指针(64位操作系统才支持)的高位来标识对象的可达性,其中第44位到47位标识GC信息

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_对象引用_04

源码查看:

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_对象引用_05

4.4、GC标记过程

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_G1_06

1、初始标记

扫描所有线程栈的根节点,然后再扫描根节点直接引用的对象并进行标记。这个阶段需要停顿所有的应用线程(STW),但由于只扫描根对象直接引用的对象,所以停顿时间很短。停顿时间高度依赖根节点的数量,从JDK16开始,已经解决了此问题:https://malloc.se/blog/zgc-jdk16

2、并发标记/并发对象重定位

第1个GC周期:并发遍历上一次标记下引用的对象并标记。

第2个GC周期:并发遍历的过程中,顺便把上周期"并发迁移"阶段迁移的对象指针修正指向到新分区。

3、标记结束/再标记

标记上一次标记过程新产生的对象。并发标记过程中,应用线程可能会产生一些新对象,所以需要再标记出来。这个阶段需要停顿所有的应用线程。(STW),但由于只标记新增的对象,数量很少,所以停顿时间很短。

4、并发转移准备

为对象转移做一些前置准备,比如引用处理、弱引用清理和重定位集选择等。

5、转移开始/初始转移

迁移根节点直接引用的对象到新分区,这个阶段需要停顿所有的应用线程(STW),但由于只迁移根节点直接引用的对象,所以停顿时间很短。

6、并发迁移

并发迁移“并发标记”阶段标记的对象到新分区(对象引用指针未修改,仍指向旧分区)。

4.5、几个问题说明

1、为何并发转移阶段,对象已转移至新分区后,却没有修改线程栈上实际的引用,依然指向旧分区?

因为如果此时再扫描线程栈,修改引用地址,要扫描的量太大,效率太低。

刚好下一个GC周期也要进行扫描标记,可以利用扫描标记的时间,同时把对象引用修正指向到新分区,以此提升效率,减少停顿时间

2、并发转移阶段对象已迁移,但引用指针仍指向旧分区,如何保证旧分区被清理后对象仍然可以访问?

•由于未修改对象引用指针,为防止旧分区被清理,导致对象找不到的问题,此处引入了读屏障和转发表

•转发表记录了对象从旧位置到新位置的映射关系,实现类似一个hash表,key是旧分区的位置,value是新分区的位置,此时当访问旧位置的对象时,通过转发表可以获取新位置。这样可以避免在整个堆空间中更新对象引用的开销,因为只需要更新转发表中的条目即可。

•读屏障的作用是在读取对象引用时,检查对象的标记状态并获取转发表中的映射关系。通过读屏障,ZGC能够在读取对象引用时,将访问重定向到新位置,以确保对象的访问仍然有效。如下图:每次读取引用时会触发一次读屏障

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_应用程序_07

5、GC全流程示意图

此示意图依据JDK11的ZGC理念绘制,尽管在JDK11至JDK17的多个版本迭代过程中,部分技术实现或许发生了变动,然而核心原理依旧保持不变。

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_应用程序_08

6、GC日志分析

下面是我压测过程的GC日志,【STW】表示暂停业务线程执行GC,【并发】表示不暂停业务线程并发执行GC,可以看到STW停顿时间很短

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_对象引用_09

我们再把上面的关键日志贴到到GC示意图中来分析实际的GC过程,可以发现总停顿时间只有0.07ms,符合官方说的小于1ms

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手 | 京东云技术团队_对象引用_10

7、ZGC如何调优

结论: 1、ZGC 被设计为自适应且需要最少的手动配置。在 Java 程序执行期间,ZGC 通过调整代大小、扩展 GC 线程数量以及调整保有阈值来动态适应工作负载。主要的调整旋钮是增加最大堆大小。 2、不再需要调整–Xmn、–XX:TenuringThreshold 和–XX:ConcGCThreads(动态调整,JDK17开始)等 3、只需要设置:–Xmx -XX:+UseZGC

7.1、设置堆大小

ZGC 最重要的调整选项是设置最大堆大小,您可以使用-Xmx命令行选项进行设置。由于 ZGC 是并发收集器,因此您必须选择最大堆大小,以便堆可以容纳应用程序的实时集,并且堆中有足够的空间以允许在 GC 运行时处理分配。需要多少空间很大程度上取决于分配率和应用程序的实时设置大小。一般来说,给 ZGC 的内存越多越好。但同时,浪费内存也是不可取的,因此关键在于在内存使用量和 GC 需要运行的频率之间找到平衡。

7.2、设置并发GC线程

不需要特意设置GC线程数,程序会自动调整。

我们可能想要考虑的第二个调整选项是设置并发 GC 线程的数量 ( -XX:ConcGCThreads=)。ZGC 具有启发式方法来自动选择此数字。这种启发式方法通常效果很好,但根据应用程序的特性,可能需要进行调整。该选项本质上决定了应该为 GC 提供多少 CPU 时间。给予太多,GC 将从应用程序中窃取过多的 CPU 时间。如果设置太少,应用程序分配垃圾的速度可能会快于 GC 收集垃圾的速度。

从 JDK 17 开始,ZGC 动态扩展和缩减并发 GC 线程数。这使得您更不需要调整 GC 线程的并发数量。

一般来说,如果低延迟(即低应用程序响应时间)对您的应用程序很重要,那么**永远不要过度配置您的系统。**理想情况下,您的系统的 CPU 利用率不应超过 70%。

7.3、ZGC特有参数配置

正如我上面说的,大部分情况的都不需要进行调优,特殊情况设置最好结合压测情况。

-XX:ZAllocationSpikeTolerance

表示ZGC 垃圾回收器在检测到内存分配波动时的容忍度,默认50,越小越敏感,会更快地对内存分配波动做出反应,可能会导致更频繁的垃圾回收。一般不需要手动设置,应对突发流量时,可以考虑设置。

-XX:ZCollectionInterval

该参数用于设置 ZGC 的垃圾回收间隔时间。默认值为 4s,表示 ZGC 每 4 秒进行一次垃圾回收。您可以根据应用程序的性能需求和停顿时间目标进行调整。

-XX:ZProactive

该参数用于启用或禁用 ZGC 的主动模式。默认情况下,ZGC 处于主动模式,以最大程度地减少停顿时间。如果将该参数设置为 false,则 ZGC 将进入被动模式,可能会导致更长的停顿时间,但可以提高吞吐量。

-XX:UseLargePages

开启大页面,此选项依赖Linux的内核,且需要用root去开启。理论上堆内存越大,回收效率越好。

7.4、开启 GC 日志

要启用基本日志记录(每个 GC 输出一行):-Xlog:gc:gc.log

要启用对调优/性能分析有用的 GC 日志记录:-Xlog:gc*:gc.log,其中 gc* 表示记录包含该gc标签的所有标签组合, :gc.log 表示将日志写入名为 gc.log

8、总结

通过本文的介绍,我们大致了解了ZGC的核心原理、日志分析方法以及调优技巧。总的来说,ZGC作为一种现代化的垃圾回收器,它为大规模应用程序的性能和可用性带来了显著的提升,希望本文能帮助大家更好的理解和应用ZGC。

作者:京东科技 曲振富

来源:京东云开发者社区 转载请注明来源

标签:并发,JDK17,对象,调优,GC,内存,线程,ZGC
From: https://blog.51cto.com/u_15714439/9177812

相关文章

  • 【积微成著】性能测试调优实战与探索(存储模型优化+调用链路分析)| 京东物流技术团队
    一、前言性能测试之于软件系统,是保障其业务承载能力及稳定性的关键措施。以软件系统的能力建设为主线,系统能力设计工作与性能测试工作,既有先后之顺序,亦有相互之影响。以上,在性能测试的场景决策,架构分析、流量分析、压测实施和剖解调优等主要环节中,引发对于系统能力底盘夯实和测试策......
  • JVM如何线上调优?
    晚上8点是我和们的业务高峰,—到高峰的时候,发现TP8的蚝时会变高,有明显的手剩,通过排查发现内存使用率也会增大,然后再释放,其他各项指标证正常,于是怀疑是GC导致的,观察服务器的GC情况,发现yongGC情况如下,大概每5分钟,GC55次,峰值最高可以达到20次。FiulGC比较频繁,每5分钟大概0.5次,峰值8次......
  • 大厂性能测试监控指标及分析调优指南
    一、哪些因素会成为系统的瓶颈CPU:如果存在大量的计算,他们会长时间不间断的占用CPU资源,导致其他资源无法争夺到CPU而响应缓慢,从而带来系统性能问题,例如频繁的FullGC,以及多线程造成的上下文频繁的切换,都会导致CPU繁忙,一般情况下CPU使用率<75%比较合适。内存:Java内存一般是通过jvm......
  • JVM-实战篇-GC调优
    1GC调优GC调优指的是对垃圾回收进行调优。GC调优的主要目标是避免由垃圾回收引起的程序性能下降。GC调优的核心分为三部分:通过JVM参数的设置;特定垃圾回收器的JVM参数的设置;解决由频繁的FULLGC引起的程序性能问题。GC调优没有唯一的标准答案,重点学习调优的工具和方法。......
  • JVM-实战篇-内存调优
    1内存溢出和内存泄漏概念:内存泄漏:在Java中如果不在使用一个对象,但是该对象依然在GCROOT的引用链上,这个对象就不会被垃圾回收器回收,这种情况就称之为内存泄漏。内存泄漏绝大多数情况都是由堆内存泄漏引起的,所以后续没特殊说明讨论的均为堆内存泄漏。若持续发生内存泄漏,不管有......
  • Ubuntu22安装graalvm JDK17+Tomcat9设置自启动
    graalvmJdk17安装参考 https://blog.csdn.net/weixin_46551671/article/details/134264889 Tomcat安装下载地址https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.84/bin/apache-tomcat-9.0.84.tar.gz安装下载安装包,将安装包上传到/usr/local/目录下执行以下命令tar......
  • 人工智能大模型原理与应用实战:预训练模型的优化与调优
    1.背景介绍人工智能(ArtificialIntelligence,AI)是计算机科学的一个分支,研究如何使计算机具有智能,能够理解人类语言、进行问题解决、学习和自主决策等。随着数据量的增加和计算能力的提升,深度学习(DeepLearning)成为人工智能的核心技术之一,它能够自动学习表示和特征,从而实现人类级别......
  • Java应用怎么调优?【转】
    一、Java应用调优的关键指标调优之前首先我们要知道怎样才算是“优”,不能笼统的说我的程序性能很好,所以就需要有一个具体的指标来衡量性能情况,而在JVM里面衡量性能两个指标分别“吞吐量”和“停顿时间”。吞吐量程序运行过程中执行两种任务,分别是执行业务代码和进行垃圾回收,吞......
  • TSNE 的参数调优: 实现更好的数据可视化效果
    1.背景介绍T-SNE(t-distributedStochasticNeighborEmbedding)是一种用于非线性降维的算法,主要用于数据可视化。它可以将高维数据降至二维或三维,使数据点之间的距离尽可能保持不变,从而实现数据的可视化。T-SNE算法的核心思想是通过一个高斯分布的概率模型来描述数据点之间的相似性......
  • 【JVM调优】内存溢出+CPU占用过高:问题排查+解决方案+复盘
    前言最近刚上线了一款社交项目,运行十多天后(运营持续每天推量),发现问题:系统OOM(资源不能被释放)导致服务器频繁且长时间FGC导致服务器CPU持续飚高日志中内存溢出:java.lang.OutOfMemoryError:Javaheapspace程序十分卡顿,严重影响用户使用从以下方面,为大家分享此次问题解决流程问题出......