ZGC垃圾收集器介绍

ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,它的设计目标包括:

  • 停顿时间不超过10ms;
  • 停顿时间不会随着堆的大小,或者活跃对象的大小而增加;
  • 支持8MB~4TB级别的堆(未来支持16TB)

ZGC的目标是希望在尽可能对吞吐量影响不太大的前提下 ,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟。

在ZGC算法中,并没有分代的概念,所以就不存在Young GC、Old GC,所有的GC行为都是Full GC

Region布局

先从ZGC的内存布局说起。

和G1一样,ZGC也采用基于Region的堆内存布局,但与G1不同的是,ZGC的Region具有动态性——动态创建和销毁,以及动态的区域容量大小。

在x64硬件平台下,ZGC的Region可以有小、中、大、三类容量:

  • 小型Region(Small Region):容量固定为2MB,用于放置小于256KB的小对象。
  • 中型Region(Medium Region):容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。
  • 大型Region(Large Region):容量不固定,可以动态变化,但必须为2MB的整数倍,用于放置4MB或以上的大对象。每个大型Region中只会存放一个大对象,这也预示着虽然名字叫作「大型Region」,但它的实际容量完全有可能小于中型Region,最小容量可低至4MB。大型Region在ZGC的实现中是不会被重分配的,因为复制一个大对象的代价非常高昂。

读屏障

之前的GC都是采用写屏障(Write Barrier),而ZGC采用的是读屏障。

读屏障(Load Barriers)类似于 Spring AOP 的前置通知。

在ZGC中,当读取处于重分配集的对象时,会被读屏障拦截,通过转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象,ZGC将这种行为叫做指针的「自愈能力」。

这样就算GC把对象移动了,读屏障也会发现并修正指针,于是应用代码就永远都会持有更新后的有效指针,而且不需要STW,类似JDK里的CAS自旋,读取的值发现已经失效了,需要重新读取。

好处是:第一次访问旧对象访问会变慢,但也只会有一次变慢,当「自愈」完成后,后续访问就不会变慢了。

正是因为Load Barriers的存在,所以会导致配置ZGC的应用的吞吐量会变低。不过这点开销是值得的。

染色指针

ZGC收集器有一个标志性的设计是它采用的「染色指针」技术。

ZGC 出现之前, GC 信息保存在对象头的 Mark Word 中,如对象的哈希码、分代年龄、锁记录等就是这样存储的。

追踪式收集算法的标记阶段就可能存在只跟指针打交道而不必涉及指针所引用的对象本身的场景。

例如对象标记的过程中需要给对象打上三色标记,这些标记本质上就只和对象的引用有关,而与对象本身无关。

而ZGC的染色指针将这些信息直接标记在引用对象的指针上。

染色指针是一种直接将少量额外的信息存储在指针上的技术,Linux下64位指针的高18位不能用来寻址,ZGC的染色指针技术盯上了这剩下的46位指针宽度,将其高4位提取出来存储四个标志信息

当然,由于这些标志位进一步压缩了原本就只有46位的地址空间,也直接导致ZGC能够管理的内存不可以超过4TB(2的42次幂)

JVM 可以从指针上直接看到对象的三色标记状态(Marked0、Marked1)、是否进入了重分配集(Remapped)、是否需要通过 finalize 方法来访问到(Finalizable)。

18位:预留给以后使用;
1位:Finalizable标识,此位与并发引用处理有关,它表示这个对象只能通过finalizer才能访问;
1位:Remapped标识,设置此位的值后,对象未指向relocation set中(relocation set表示需要GC的Region集合);
1位:Marked1标识;
1位:Marked0标识,和上面的Marked1都是标记对象用于辅助GC;
42位:对象的地址(所以它可以支持2^42=4T内存);

染色指针的优势

染色指针主要有三大优势:

  • 染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用掉,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理。理论上只要还有一个空闲Region,ZGC就能完成收集。
  • 染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量,ZGC只使用了读屏障。因为信息直接维护在指针中。
  • 染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,以便日后进一步提高性能
    如果开发了前18位指针,既可以腾出已用的4个标志位,将ZGC可支持的最大堆内存从4TB拓展到64TB,也可以利用其余位置再存储更多的标志,譬如存储一些追踪信息来让垃圾收集器在移动对象时能将低频次使用的对象移动到不常访问的内存区域。

 

运作过程

ZGC的运作过程大致可划分为以下四个大的阶段。全部四个阶段都是可以并发执行的,仅是两个阶段中间会存在短暂的停顿小阶段,这些小阶段,譬如初始化GC Root直接关联对象的Mark Start,ZGC的运作过程具体如图所示。

  • 并发标记(Concurrent Mark):并发标记是遍历对象图做可达性分析的阶段。与G1、Shenandoah不同的是,ZGC的标记是在指针上而不是在对象上进行的,标记阶段会更新染色指针中的Marked 0、Marked 1标志位

  • 并发预备重分配(Concurrent Prepare for Relocate):这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集(Relocation Set)。

    重分配集与G1收集器的回收集(Collection Set)还是有区别的,ZGC划分Region的目的并非为了像G1那样做收益优先的增量回收。

    相反,ZGC每次回收都会扫描所有的Region,用范围更大的扫描成本换取省去G1中记忆集的维护成本

    因此,ZGC的重分配集只是决定了里面的存活对象会被重新复制到其他的Region中,里面的Region会被释放,而并不能说回收行为就只是针对这个集合里面的Region进行,因为标记过程是针对全堆的。

    此外,在JDK 12的ZGC中开始支持的类卸载以及弱引用的处理,也是在这个阶段中完成的。

  • 并发重分配(Concurrent Relocate):重分配是ZGC执行过程中的核心阶段,这个过程要把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系

    得益于染色指针的支持,ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程此时并发访问了位于重分配集中的对象,这次访问将会被预置的内存屏障所截获,然后立即根据Region上的转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象。

    ZGC将这种行为称为指针的「自愈”(Self-Healing)」能力。这样做的好处是只有第一次访问旧对象会陷入转发,也就是只慢一次,对比Shenandoah的Brooks转发指针,那是每次对象访问都必须付出的固定开销,简单地说就是每次都慢。

    因此ZGC对用户程序的运行时负载要比Shenandoah来得更低一些。

    还有另外一个直接的好处是由于染色指针的存在,一旦重分配集中某个Region的存活对象都复制完毕后,这个Region就可以立即释放用于新对象的分配(但是转发表还得留着不能释放掉),哪怕堆中还有很多指向这个对象的未更新指针也没有关系,这些旧指针一旦被使用,它们都是可以自愈的。

  • 并发重映射(Concurrent Remap):重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用。

    这一点从目标角度看是与Shenandoah并发引用更新阶段一样的,但是ZGC的并发重映射并不是一个必须要「迫切」去完成的任务,因为前面说过,即使是旧引用,它也是可以自愈的,最多只是第一次使用时多一次转发和修正操作。

    重映射清理这些旧引用的主要目的是为了不变慢(还有清理结束后可以释放转发表这样的附带收益),所以说这并不是很「迫切」。因此,ZGC很巧妙地把并发重映射阶段要做的工作,合并到了下一次垃圾收集循环中的并发标记阶段里去完成,反正它们都是要遍历所有对象的,这样合并就节省了一次遍历对象图的开销。一旦所有指针都被修正之后,原来记录新旧对象关系的转发表就可以释放掉了。

ZGC几乎整个收集过程都全程可并发,短暂停顿也只与GC Roots大小相关而与堆内存大小无关,因而同样实现了任何堆上停顿都小于十毫秒的目标。

总结:

相比G1、Shenandoah等先进的垃圾收集器,ZGC在实现细节上做了一些不同的权衡选择。

譬如G1需要通过写屏障来维护记忆集,才能处理跨代指针,得以实现Region的增量回收。记忆集要占用大量的内存空间,写屏障也对正常程序运行造成额外负担,这些都是权衡选择的代价。

ZGC就完全没有使用记忆集,它甚至连分代都没有,连像CMS中那样只记录新生代和老年代间引用的卡表也不需要,因而完全没有用到写屏障,所以给用户线程带来的运行负担也要小得多。

可是,有优就有劣,ZGC的这种选择也限制了它能承受的对象分配速率不会太高。

因为ZGC四个阶段都支持并发,如果分配速率高,将创造大量的新对象,这就产生了大量的浮动垃圾。如果这种高速分配持续维持的话,回收到的内存空间持续小于期间并发产生的浮动垃圾所占的空间,堆中剩余可腾挪的空间就越来越小了。

目前唯一的办法就是尽可能地增加堆容量大小,获得更多喘息的时间。但是若要从根本上提升ZGC能够应对的对象分配速率,还是需要引入分代收集,让新生对象都在一个专门的区域中创建。所以分代算法有利有弊。

最后对本篇文章做一个提炼总结:

ZGC(Z Garbage Collector)是一个面向并行、无停顿时间的垃圾回收器,它作为JDK11的一部分首次引入,并且在JDK15开始被正式视为生产就绪级别:

  1. 出现意义:面对现代硬件环境中大内存、多核心的趋势以及微服务等新型应用的需求,如低延迟和高吞吐量,传统的垃圾收集器(如Parallel GC和CMS等)可能无法满足要求,尤其在处理多达数TB内存的情况下。在这样的背景下,ZGC应运而生,其设计目标是处理大型堆内存,同时将停顿时间限制在10ms以内,并且不牺牲整体吞吐量。
  2. 主要特点:其采用读屏障(Read Barrier)和染色指针(Colored Pointer)技术,实现了可扩展性,可以从几百MB到4TB的Java堆大小进行高效处理。此外,ZGC能够实现几乎所有的工作都在并行和并发阶段完成,包括对象可达性的标记、对象重定位和引用更新等操作。这使得它能够大幅度地降低垃圾收集带来的停顿时间。
  3. 适用场景:ZGC非常适合需要大内存,低延时,以及可预测的响应时间的系统,例如,金融交易、游戏、广告科技等领域的应用。
  4. 局限性:尽管ZGC有许多优点,但也有一些局限性。例如,由于其复杂的实现,对JVM的代码入侵较深,可能会与一些JVM特性或者优化手段不兼容。另外,尽管ZGC的暂停时间很短,但并发处理可能占用较多的CPU资源,所以在CPU敏感的环境下,其表现可能不如其他垃圾收集器。

总的来说,ZGC是一种创新的垃圾收集器,它解决了大内存和低延迟之间的矛盾,为构建现代大规模、高性能的Java应用提供了更多可能。

通过美团分享的文章,zgc也有适用和非使用的场景,

在Zeus服务不同集群中,ZGC在低延迟(TP999 < 200ms)场景中收益较大:

  • TP999:下降12~142ms,下降幅度18%~74%。
  • TP99:下降5~28ms,下降幅度10%~47%。

超低延迟(TP999 < 20ms)和高延迟(TP999 > 200ms)服务收益不大,原因是这些服务的响应时间瓶颈不是GC,而是外部依赖的性能,对吞吐量优先的场景,ZGC可能并不适合。例如,Zeus某离线集群原先使用CMS,升级ZGC后,系统吞吐量明显降低。究其原因有二:第一,ZGC是单代垃圾回收器,而CMS是分代垃圾回收器。单代垃圾回收器每次处理的对象更多,更耗费CPU资源;第二,ZGC使用读屏障,读屏障操作需耗费额外的计算资源。

所以可以参考自己的业务,是否适合升级

参考:

新一代垃圾回收器ZGC的探索与实践 - 美团技术团队

 https://www.cnblogs.com/booksea/p/17665685.html

Java ZGC垃圾收集器(算法及回收原理详解) – mikechen

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

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

相关文章

微信小程序 获取地址信息(uniapp)

参考API地址&#xff1a;微信小程序JavaScript SDK | 腾讯位置服务 <script> // 引入SDK核心类&#xff0c;js文件根据自己业务&#xff0c;位置可自行放置var QQMapWX require(../../js/uploadImg/qqmap-wx-jssdk.js);export default {data(){return{qqmapsdk:}},onL…

【HarmonyOS4.0】第四篇-ArkUI基础实战

一、ArkUI框架简介 ArkUI开发框架是方舟开发框架的简称&#xff0c;它是一套构建 HarmonyOS / OpenHarmony 应用界面的声明式UI开发框架&#xff0c;它使用极简的UI信息语法、丰富的UI组件以及实时界面语言工具&#xff0c;帮助开发者提升应用界面开发效率 30%&#xff0c;开发…

Swift单元测试Quick+Nimble

文章目录 使用QuickNimble1、苹果官方测试框架XCTest的优缺点2、选择QuickNimble的原因&#xff1a;3、QuickNimble使用介绍集成&#xff1a;Quick关键字说明&#xff1a;Nimble中的匹配函数等值判断&#xff1a;使用equal函数是否是同一个对象&#xff1a;使用beIdenticalTo函…

Android14之刷机模式总结(一百七十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

电脑文件mfc100u.dll丢失的解决方法分析,怎么修复mfc100u.dll靠谱

mfc100u.dll丢失了要怎么办&#xff1f;其实很多人都遇到过这样的电脑故障吧&#xff0c;说这个mfc100u.dll文件已经不见了&#xff0c;然后一些程序打不开了&#xff0c;那么这种情况我们要怎么解决呢&#xff1f;今天我们就来给大家详细的说说mfc100u.dll丢失的解决方法。 一…

【unity小技巧】实现没有动画的FPS武器摇摆和摆动效果

文章目录 前言开始完结 前言 添加程序摇摆和摆动是为任何FPS游戏添加一些细节的非常简单的方法。但是并不是所以的模型动画都会配有武器摆动动画效果&#xff0c;在本文中&#xff0c;将实现如何使用一些简单的代码实现武器摇摆和摆动效果&#xff0c;这比设置动画来尝试实现类…

Golang中for和for range语句的使用技巧、对比及常见的避坑

前言 基础语法不再赘述&#xff0c;写这个原因是之前的某次面试被问道了&#xff0c;我知道会导致问题但具体答下来不是很通顺。再回想自己开发过程中&#xff0c;很多地方都是使用到了for/for range&#xff0c;但是却从没注意过一些细节&#xff0c;因此专门学习一下进行记录…

K8S中SC、PV、PVC的理解

存储类&#xff08;StorageClass&#xff09;定义了持久卷声明&#xff08;PersistentVolumeClaim&#xff09;所需的属性和行为&#xff0c;而持久卷&#xff08;PersistentVolume&#xff09;是实际的存储资源&#xff0c;持久卷声明&#xff08;PersistentVolumeClaim&#…

平衡搜索二叉树(AVL树)

前言 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查 找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述…

vue中鼠标拖动触发滚动条的移动

前言 在做后端管理系统中&#xff0c;像弹窗或大的表单时&#xff0c;经常会有滚动条的出现&#xff0c;但有些时候如流程、图片等操作时&#xff0c;仅仅使用鼠标拖动滚动条操作不太方便&#xff0c;如果使用鼠标拖拽图片或容器来触发滚动条的移动就比较方便了 功能设计 如…

LeetCode(454)四数相加 II⭐⭐

给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) &#xff0c;使得 A[i] B[j] C[k] D[l] 0。 为了使问题简单化&#xff0c;所有的 A, B, C, D 具有相同的长度 N&#xff0c;且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 到 2^28 - 1 之间&#…

Pinia处学习

修改数据的三种方式: mutate本次变化的数据,state时store中的数据

大模型训练营Day2 homework

1.使用 InternLM-Chat-7B 模型生成 300 字的小故事 2.熟悉 hugging face 下载功能&#xff0c;使用 huggingface_hub python 包&#xff0c;下载 InternLM-20B 的 config.json 文件到本地&#xff1a; 下面开始下载和推广大模型的相关的包&#xff1a; 这里需要在本地上&…

uniapp自定义底部导航栏

1.新建 nav-custom.vue组件 <template><view class"nav-box" :style"{height:heightpx,background:bgColor}"><!-- 自定义导航栏 --><view class"status_bar" :style"{height:statusBarHeightpx}"><!-- u…

@Transactional 事务注解

第一、先简单介绍一下Spring事务的传播行为 所谓事务的传播行为是指&#xff0c;如果在开始当前事务之前&#xff0c;一个事务上下文已经存在&#xff0c;此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量&…

HarmonyOS应用开发学习笔记 UIAbility组件间交互 UIAbility启动,页面跳转结果回调

1、 HarmoryOS Ability页面的生命周期 2、 Component自定义组件 3、HarmonyOS 应用开发学习笔记 ets组件生命周期 4、HarmonyOS 应用开发学习笔记 ets组件样式定义 Styles装饰器&#xff1a;定义组件重用样式 Extend装饰器&#xff1a;定义扩展组件样式 5、HarmonyOS 应用开发…

MySQL的体系结构(超全总结版)

MySQL组成 连接池组件管理服务和工具组件SQL接口组件查询分析器组件优化器组件缓冲组件插件式存储引擎物理文件 存储引擎 InnoDB存储引擎 主要面向OLTP(在线事务处理)方面的应用&#xff0c;特点是行锁设计、支持外键&#xff0c;默认情况下读取操作不会产生锁。通过使用多…

Centos7下升级gcc/g++版本(简单 + 避坑)

在 Centos7 下&#xff0c;使用 yum 安装高版本的 gcc/g 最简单&#xff1a; yum -y install centos-release-scl yum -y install devtoolset-11-gcc devtoolset-11-gcc-c devtoolset-11-binutils需要安装哪个个版本的gcc/g&#xff0c;就把数字替换成对应的版本号。例如上面代…

船舶3d虚拟展厅线上制作降低展示成本

VR线上虚拟展厅漫游搭建平台是一种新兴的技术&#xff0c;它能够为用户提供更加真实、沉浸式的体验。目前&#xff0c;市场上有许多公司提供VR线上虚拟展厅漫游搭建平台技术服务&#xff0c;为客户提供全方位的支持。 首先&#xff0c;这些公司通常会提供专业的策划和设计服务。…

TCP三次握手过程?

TCP三次握手过程&#xff1f; 分享 回答 1 浏览 3662 一颗小胡椒 2 CISM-WSE CISP-PTS 三次握手是 TCP 连接的建立过程。在握手之前&#xff0c;主动打开连接的客户端结束 CLOSE 阶段&#xff0c;被动打开的服务器也结束 CLOSE 阶段&#xff0c;并进入 LISTEN 阶段。随后进入…