垃圾收集器必问系列—ZGC

news2024/11/16 13:36:06

本文已收录至Github,推荐阅读 👉 Java随想录

人的一切痛苦,本质上都是对自己的无能的愤怒。——王小波

文章目录

    • Region布局
    • 读屏障
    • 染色指针
      • 染色指针的优势
    • 运作过程
    • ZGC的优缺点

ZGC有人称它为Zero GC,其实“Z”并非什么专业名词的缩写,这款收集器的名字就叫作Z Garbage Collector。根据OpenJDK官方网站的说明ZGC其实并没有什么特殊意义,就是一个名字而已。起初只是为了致敬ZFS 文件系统,表示ZGC与ZFS一样都是革命性的,是一个跨时代的产品。更像是一种崇拜命名法。所以ZGC就是要做革命性的与以往的垃圾回收器性能上有很大提高的GC。

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大小相关而与堆内存大小无关,因而同样实现了任何堆上停顿都小于十毫秒的目标

ZGC的优缺点

相比G1、Shenandoah等先进的垃圾收集器,ZGC在实现细节上做了一些不同的权衡选择,譬如G1需要通过写屏障来维护记忆集,才能处理跨代指针,得以实现Region的增量回收。记忆集要占用大量的内存空间,写屏障也对正常程序运行造成额外负担,这些都是权衡选择的代价。ZGC就完全没有使用记忆集,它甚至连分代都没有,连像CMS中那样只记录新生代和老年代间引用的卡表也不需要,因而完全没有用到写屏障,所以给用户线程带来的运行负担也要小得多

可是,有优就有劣,ZGC的这种选择也限制了它能承受的对象分配速率不会太高,因为ZGC四个阶段都支持并发,如果分配速率高,将创造大量的新对象,这就产生了大量的浮动垃圾。如果这种高速分配持续维持的话,回收到的内存空间持续小于期间并发产生的浮动垃圾所占的空间,堆中剩余可腾挪的空间就越来越小了。目前唯一的办法就是尽可能地增加堆容量大小,获得更多喘息的时间。但是若要从根本上提升ZGC能够应对的对象分配速率,还是需要引入分代收集,让新生对象都在一个专门的区域中创建。所以分代算法有利有弊。


如果本篇博客有任何错误和建议,欢迎给我留言指正。文章持续更新。

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

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

相关文章

vue前端框架课程笔记(二)

目录计算属性问题引入插值语法实现methods配置属性实现computed配置属性一些问题getter与setter注意监视属性问题引入computed和methods实现watch属性watch属性简介computed与watch的比较注意本博客参考尚硅谷官方课程,详细请参考 【尚硅谷bilibili官方】 本博客以…

千峰网络安全笔记(前三讲)

典中典 《c语言从研发到脱发》 《C从入门到放弃》 《Java从跨平台到跨行业》 《Ios开发从入门到下架》 《Android开发大全——从开始到转行》 《PHP由初学至搬砖》 《黑客攻防:从入门到入狱》 《Mysql从删库到跑路》 《服务器运维管理从网络异常到硬盘全红》 《服务器运维管理…

CentOS 8 中dnf管理器如何仅下载不安装软件

在某些情况下,我们希望从命令行下载特定或一组 RPM 包而不安装它。虽然我们可以使用 wget 命令下载,但 wget 不会下载安装包的依赖项。在 CentOS 8 中DNF(或 yum)是一个命令行包管理工具。使用 DNF我们可以安装、更新和删除 rpm 包…

HTTP协议学习

一、http请求协议1、常见请求头accept:浏览器通过这个头告诉服务器,它所支持的数据类型Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式Accept-Language…

114.简单的动态切换app的图标

1.第一步 通过activity-alias别名实现&#xff0c;manifest 这里写的是一个默认的图标Default和一个需要切换的图标Test&#xff0c;以及一个默认的首页面HomeActivity&#xff1a; <!-- 默认的图标--> <activity-aliasandroid:name".activity.Default"and…

C语言入门教程||C语言 运算符||C语言 判断

C语言 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符&#xff0c;并提供了以下类型的运算符&#xff1a; 算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符 本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算…

基于android的大学生图书管理系统app

需求信息&#xff1a; 1.学生用户端 查询图书。学生用户可以对馆内图书资料进行简单和高级的查询。 预约图书。当查询时发现要借阅的图书已被借阅&#xff0c;可以提前预约。 挂失图书。图书不慎丢失&#xff0c;可以在学生端实现挂失。 2.管理员端 学生用户管理。管理员可以对…

LeetCode——2309. 兼具大小写的最好英文字母

一、题目 给你一个由英文字母组成的字符串 s &#xff0c;请你找出并返回 s 中的最好英文字母。返回的字母必须为大写形式。如果不存在满足条件的字母&#xff0c;则返回一个空字符串。 最好 英文字母的大写和小写形式必须 都 在 s 中出现。 英文字母 b 比另一个英文字母 a …

Python 操作pdf(pdfplumber读取PDF写入Exce)

1. Python 操作pdf(pdfplumber读取PDF写入Exce) 1.1 安装pdfplumber模块库: 安装pdfplumber: pip install pdfplumber 复制代码 pdfplumber.PDF类 pdfplumber.PDF类表示单个PDF ,并具有两个主要属性: 属性说明pdf.metadata从PDF的Info中获取元数据键/值对字典。通常包括&q…

【HBase——陌陌海量存储案例】4. Apache Phoenix 介绍与安装

5. 性能问题 Hbase默认只支持对行键的索引&#xff0c;那么如果要针对其它的列来进行查询&#xff0c;就只能全表扫描之前介绍的查询是使用scan filter组合来进行查询的&#xff0c;但查询地效率不高&#xff0c;因为要进行顺序全表扫描而没有其他索引。如果数据量较大&#…

51单片机学习笔记-8 DS1302实时时钟

8 DS1302实时时钟 [toc] 注&#xff1a;笔记主要参考B站江科大自化协教学视频“51单片机入门教程-2020版 程序全程纯手打 从零开始入门”。 8.1 芯片介绍&#xff1a;DS1302 RTC(Real Time Clock)实时时钟&#xff0c;是一种集成电路&#xff0c;通常称为时钟芯片。常见的时…

AIGC在营销图片生成技术综述

基于文本生成素材imagen分析用户输入的文本并使用T5-XXL进行编码。嵌入在 AI 中的文本首先被转换为分辨率为64x64像素的小图像。Imagen进一步利用文本条件超分辨率扩散模型对图像进行6464的上采样&#xff0c;然后这个图像继续增长并最终形成。Imagen 的开发者谷歌研究的大脑团…

剑指 Offer 第11天 第12天

目录 剑指 Offer 18. 删除链表的节点 剑指 Offer 22. 链表中倒数第k个节点 剑指 Offer 25. 合并两个排序的链表 剑指 Offer 52. 两个链表的第一个公共节点 剑指 Offer 18. 删除链表的节点 给定单向链表的头指针和一个要删除的节点的值&#xff0c;定义一个函数删除该节点。…

台式电脑怎么连wifi,1分钟轻松弄懂

电脑已经成为了各位小伙伴日常生活中经常会使用的工具。笔记本电脑连接wifi很简单&#xff0c;相信很多小伙伴都会&#xff0c;可是面对台式电脑&#xff0c;很多小伙伴就不知道怎么连wifi了。台式电脑怎么连wifi&#xff1f;别担心&#xff0c;1分钟教你轻松弄懂。 台式电脑怎…

MQ消息队列

mq1、什么是消息队列1.1 MQ基本框架1.2 消息队列的优点1.3 消息队列的缺点1.4 消息队列比对2、RabbitMQ2.1、RabbitMQ如何保证消息不被重复消费2.2、RabbitMQ如何保证消息不丢失2.2.1 生产者丢数据2.2.2 消息队列丢数据2.2.3 消费者丢数据2.3、RabbitMQ如何保证消息有序2.4、Ra…

基于蜣螂算法改进的随机森林分类算法-附代码

基于蜣螂算法改进的随机森林分类算法 - 附代码 文章目录基于蜣螂算法改进的随机森林分类算法 - 附代码1.数据集2.RF模型3.基于蜣螂算法优化的RF4.测试结果5.Matlab代码摘要&#xff1a;为了提高随机森林数据的分类预测准确率&#xff0c;对随机森林中的树木个数和最小叶子点数参…

SVN关联PyCharm使用

前言 本人因为要搭建一个自动化测试的框架&#xff0c;编程语言选择的python&#xff0c;python编辑器选择的PyCharm&#xff0c;代码管理工具使用的SVN。为了方便协作开发&#xff0c;需要将SVN关联PyCharm进行使用。 一、SVN关联PyCharm 1.点击左上角File–>选择Settin…

mongodb的聚合操作

mongodb的聚合操作 学习目标 了解 mongodb的聚合原理掌握 mongdb的管道命令掌握 mongdb的表达式 1 mongodb的聚合是什么 聚合(aggregate)是基于数据处理的聚合管道&#xff0c;每个文档通过一个由多个阶段&#xff08;stage&#xff09;组成的管道&#xff0c;可以对每个阶…

【Python】生成本项目的requeirments.txt

有的时候&#xff0c;我们需要对自己写的项目生成一个requeirments.txt&#xff0c;方便其他使用者快速安装依赖项 参考https://www.cnblogs.com/shun7man/p/14080921.html 1.使用pip 如果你的项目本身就是在venv虚拟环境下跑的&#xff0c;那么可以直接用下面的语句生成一个依…

java集合类-List/Queue

List List集合代表一个元素有序、可重复的集合&#xff0c;集合中每个元素都有其对应的顺序索引。可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。 List接口&#xff08;被改进&#xff09;和ListIterator接口&#xff08;被改进&#xf…