【Java虚拟机】简单易懂的ZGC原理分析

简单易懂的ZGC原理分析

  • GC垃圾收集器
  • ZGC的特点
  • ZGC相关技术
    • Region
    • 染色指针 & 转发表 & 读屏障
      • 染色指针
      • 转发表
      • 读屏障
    • 内存多重映射
  • ZGC流程详解
  • ZGC与其他垃圾搜集器比较
    • 与CMS比较
    • 与G1比较

GC垃圾收集器

GC垃圾收集器的作用就是帮我们清理堆内存里面的垃圾,无需我们自己手动清理,我们只管new对象即可。

就内存自动回收这一点,相比于C和C++来说,是方便了不少。C和C++需要我们自己通过API手动申请内存,用完了需要手动释放,非常麻烦。

在这里插入图片描述

而Java虚拟机则通过GC垃圾收集器的自动内存回收功能,免去了Java程序员手动释放内存的操作,解放出Java程序员的双手。

在这里插入图片描述

有好处,自然也是有代价的。在C和C++的世界里,我们可以自由的控制内存的分配和释放,只要程序设计的好,代码没什么问题,那么就不会有什么OOM的问题。而在Java的世界里,程序员只能new对象,至于对象的回收,以及回收的时机,对于程序员来说都是不可控的,只能由GC来处理。如果程序员new对象的速度太快,GC来不及回收,那么就会OOM(内存溢出)了。

在这里插入图片描述

除了OOM以外,还有STW——stop the world。STW就是当垃圾收集器正在运行的时候,用户线程必须停止,让垃圾收集器先做完垃圾收集的工作,清理完内存中无用的对象,释放出内存空间,用户线程才可以继续运行。这中间用户线程的停止,就是STW,很显然这是会影响程序性能的,需要尽量减少STW的停顿时长。

在这里插入图片描述

因此,GC垃圾收集器也在不断的迭代升级,从Serial单线程垃圾收集器,到Parallel的多线程垃圾收集器,然后就是CMS、G1、ZGC这些可与用户线程并发执行的垃圾收集器,GC的性能不断的优化,STW时间越来越短。

在这里插入图片描述

ZGC的特点

ZGC相对于其他老的垃圾收集器,有许多优点。

首先是ZGC支持大内存垃圾收集,可以支持16TB大小内存的垃圾收集,但是GC停顿时间不会随着内存的增大而增大,也就是大内存、低停顿。

其次,ZGC使用了染色指针技术,搭配上读屏障和转发表,ZGC可以在用户线程继续运行的同时,进行内存整理(标记整理算法),注意是内存整理,这就包括了存活对象转移以及内存回收。这是CMS和G1办不到的,CMS可以与在用户线程并发执行内存回收,那是因为它使用的时标记清除算,不需要整理内存;而G1使用的是标记整理算法,因此内存回收阶段就要STW了。

ZGC还使用了内存多重映射计算,解决染色指针造成对象内存地址错乱的问题。

最后,ZGC还支持NUMA内存分配。

在这里插入图片描述

ZGC相关技术

Region

ZGC采用了基于Region的堆内存布局,堆内存被划分成一小块一小块的内存块,每一个小块就是一个Region。ZGC的Region分为大中小三种类型。

  • 小型Region:容量固定为2MB,用于放置小于256KB的小对象。
  • 中型Region:容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。
  • 大型Region:容量不固定,可以动态变化,但必须为2MB的整数倍,用于放置4MB或以上的大对象,每个大型Region中只会存放一个大对象。

在这里插入图片描述

染色指针 & 转发表 & 读屏障

染色指针能称得上是ZGC最大的特点。染色指针就是在指针上存储一些额外的信息,使得用户线程仅从指针就能得知目标对象是否已经被转移,如果目标已被转移,那么就查询转发表(记录对象转移前的地址与对象转移后的地址的映射关系),得到目标对象转移后的地址,取到目标对象,并且同时修复该指针,下次就可以直接访问到该对象,无需再查转发表。再配上读屏障,当要通过指针访问一个对象时,都插入一段上面这样逻辑的一段代码。这就实现了GC线程在整理内存(存活对象转移)的同时用户线程也能继续运行。

在这里插入图片描述

染色指针

染色指针是利用指针中的某几位去记录一些与GC垃圾收集相关的信息。在Linux下64位指针的高18位不能用来寻址,也就是说64位指针的高18位是没有用的,然后ZGC就利用低46位中的高四位来记录这些额外的信息。

在这里插入图片描述
这四个标志位分别是Finalizable、Remapped、M1、M0。

当Finalizable标志位被置为1时,表示该对象只能通过finalize()方法才能被访问到。当Remapped标志位被置为1时,表示该对象进入了重分配集,也就是说该对象已被转移走,需要查转发表得到转移后的位置。M1和M0都是用来标记一个对象在本轮垃圾收集中是否存活的,这两个标志位会交替使用,比如本轮垃圾收集用M0标记一个对象是存活对象,那么下一轮的垃圾收集就使用M1标志位标记存活对象。

在这里插入图片描述

这里可能有人会有疑问,为啥标记一个对象是否存活需要两个标志位,然后轮流交替使用呢?我们可以看一个例子:

比如本轮垃圾收集使用的标志位是M0,那么存活的对象的M0标志位都会被置为1,标记完成之后,M0标志位没有被标记为1的,就可以认为是垃圾对象,可以被清理。然后进入到下一轮垃圾收集时,就会使用M1标志位去标记存活的对象,那么在本轮垃圾收集中存活的对象的M1标志位就会被置为1,那么M1标志位没有被置为1的,就可以认为是垃圾对象,可以被清理,这里就包括了上一轮中存活的M0标志位被置为1的对象。

在这里插入图片描述

可以看到,对象的标记和垃圾清理都可以很正常的执行。如果现在没有M0和M1两个标志位,假设ZGC只使用1个标志位呢?

在这里插入图片描述

可以看到,如果第二轮垃圾收集依然使用M0,那么就识别不出上一轮存活而本轮变成垃圾的对象,这些对象因为上一轮存活,所以它们的M0标志位是被置为1的,而本轮还是使用M0标志位去标记存活对象,那么就会和这些上一轮存活而本轮变为垃圾的对象混淆了。

上面这个例子没有考虑到对象被转移的情况。实际上当一个对象被标记为存活之后,如果它被转移了,它的标志位会变成Remapped被标记为1。

在这里插入图片描述

比如上面的这个例子,第一轮存活的对象被标记为M0了,如果被转移了,那么会被标记为Remapped。然后如果它第二轮还是存活对象,那么被标记为M1。但是如果它第二轮变成了垃圾对象,那么标志位就还是Remapped,那么就会和标志位还是M0的对象一样被清理。

在这里插入图片描述

除此之外,在JVM刚启动还没有发生垃圾收集之前,新增的对象也都会被标记为Remapped。

在这里插入图片描述

那如果是在标记阶段或者标记阶段结束之后创建的对象,会被标记为什么呢?

如果一个对象在标记阶段被创建,那么它会直接被标记为存活对象(因为是刚创建的对象,所以肯定是一个有用的对象,自然就是存活对象)。比如一个对象在第一轮标记阶段时被创建,它的M0标志位就会被置为1,也就是当前标记阶段使用的标记位会被置为1;如果是在第二轮标记阶段创建的对象,由于第二轮标记阶段使用的标志位是M1,所以在第二轮标记阶段创建的对象的标志位M1会被置为1。

如果一个对象在转移阶段(也就是本轮标记结束之后,第二轮标记阶段之前)被创建的对象,Remapped标记为会被置为1。之所以把转移阶段创建的对象标记为Remapped,是因为在转移阶段创建的对象是不需要转移的,因此可以把它们视同转移后的对象。

在这里插入图片描述

转发表

可以看到,有了染色指针,我们就可以从指针上得知一个对象处于何种状态。假设我们得知一个对象是Remapped状态,那么可以判断该对象已被转移,那么我们就要查询转发表才能获取到目标对象了。

转发表记录一个对象转移前的地址与转移后的地址的映射关系,转移前的地址可以从当前指针的低42位获得,然后再根据这个转移前的地址查询转发表,就可以得到转移后的地址,然后就可以拿着这个地址去访问目标对象。

在这里插入图片描述

并且会修复指针中的错误地址为正确的地址,这个就是所谓的“指针自愈”。

在这里插入图片描述

可以看到,有了染色指针和转发表,ZGC就可以在标记结束之后,就马上进行存活对象的转移了,并且用户线程是不需要停顿的,不需要停止用户线程等待存活对象转移完毕并且修正指针指向的值,也就没有了这部分STW的时间消耗,性能就大大提升了。

读屏障

但是怎么实现每次通过指针访问堆内存的对象时,都走一下上面这个判断逻辑呢?平常我们通过指针获取一个对象,是直接通过指针中的地址值寻址到一个对象的,没有上面这个额外的判断逻辑。因此ZGC就在每次通过指针访问堆内存的对象时,都插入一个读屏障,这个读屏障,就是ZGC在通过指针访问对象的代码前插入一段额外的逻辑。

在这里插入图片描述

当然,读屏障里面的判断和处理逻辑没有那么简单,这里只是描述了访问对象时的重定位逻辑。

内存多重映射

由于使用了染色指针,指针上有四个bit位是用于标记对象状态而不是用于寻址的,然后剩下的低42位才是用来寻址的。那么不管这四个标志位中哪个标志位被标记为1,只要低42位的地址值相同,都应该寻址到同一个对象。

在这里插入图片描述

但是计算机通过指针寻址时,是不会区分标志位还是非标志位的,它会把指针视作一个整体,不同标志位得出的地址值就会不一样。那如何使得一个指针只要低42位相同,即使标志位不同,也能指向同一个对象呢?那就是内存多重映射技术。

内存多重映射其实就是把多个虚拟地址,映射到同一个物理地址。虚拟地址和物理地址是操作系统的概念,简单理解就是我们的应用程序使用的其实不是真正的物理内存,而是操作系统虚拟出来的虚拟内存,操作系统会记录虚拟内存地址到物理内存地址之间的映射关系,当我们的应用程序需要访问内存中的某个对象时,会拿着虚拟内存地址,经过操作系统的翻译,把虚拟内存地址翻译成物理内存地址,再通过物理内存地址访问到目标对象。

在这里插入图片描述

于是,只要我们建立不同虚拟内存地址到同一个物理内存地址的映射关系,就可以使得不同的虚拟内存地址都能寻址到物理内存中的同一个对象。在Linux系统里面,可以通过mmap函数把同一个对象映射到多个虚拟内存地址上,我们可以通过mmap函数完成以下操作:

在这里插入图片描述

这样,低42位地址值相同但标志位不同的指针,都能寻址到同一个目标对象。

ZGC流程详解

ZGC的一次GC分7个阶段:初始标记、并发标记、再标记、并发转移准备、初始转移、并发转移、并发重定位。

在这里插入图片描述

但是实际上一轮GC只有六个步骤,最后一步的并发重定位,ZGC会把它放到下一轮GC的并发标记这一步来一起做。

在这里插入图片描述

其中前面三步属于标记阶段,后面三步属于转移阶段。

在这里插入图片描述

然后第一、第三、第五步是有STW的,其余步骤都是与用户线程并发执行的。

在这里插入图片描述

大体流程就是这样,下面是每个步骤的详细描述。

初始标记就是简单的枚举GC Roots直接引用的对象,虽然有STW,但是速度非常块。

在这里插入图片描述

并发标记就是从GC Roots直接引用的对象开始,使用可达性分析法进行对象的标记,会把存活对象的M0标志位或M1标志位标记位1,是使用M0标志位还是M1标志位由本轮GC决定,它们会轮流被使用于存活对象的标记。并发标记的时间在整个GC流程当中算是比较长时间的一个步骤,但是它是可以与用户线程并发进行的。

在这里插入图片描述

再标记就是修正并发标记阶段用户线程继续运行导致的错标漏标等问题,与G1垃圾收集器一样,也是采用原始快照的方式。该阶段也是有STW的,但是时长也是较短的。

在这里插入图片描述

可以看到前三个阶段与CMS和G1是一样的。

并发转移准备做的事情就是分析每个Region的回收收益,给它们排个序,优先转移回收收益高的Region中的对象,然后释放该Region的内存空间。这里要注意,ZGC不会像G1那样每次只回收一部分,ZGC做的也是全量的堆内存清理,由于对象转移可以与用户线程一起并发运行,所以不会像G1那样有STW,自然就不需要像G1那样做增量收集。

在这里插入图片描述

然后就是初始转移阶段,初始转移阶段只会转移GC Roots直接引用的存活对象,这个阶段也是要STW的。

在这里插入图片描述

然后并发转移阶段就是进行真正的对象转移,把所有需要转移的对象都完成转移,这个阶段是与用户线程并发进行的。

在这里插入图片描述

这里只是做对象的转移,指针并没有修正,因此此时的指针引用都是错乱的,但是转移对象的同时会维护一个转发表。

在这里插入图片描述

如果此时我们通过指针访问一个对象时,会被读屏障拦截,判断到此时对象被标记位Remapped,就会去查询转发表取得正确的地址,并修复错误的指针。

在这里插入图片描述
对象都转移完了,就会释放边界外的内存空间。这个是标记整理算法的特点,把存活对象向一边转移,然后把边界外的内存空间一次性释放出来。

在这里插入图片描述

现在就剩下把所有的错误指针修复了。除了在转移阶段被用户线程访问而触发自愈的指针以外,其他的错误指针都是没有修复的,它们会在下一轮GC的并发标记阶段被全量修复。之所以这么设计,一方面是由于有指针自愈这个机制,错误指针修复这个事情就不那么着急了;另一方面是与并发标记一起做,就可以省掉一次遍历。

在这里插入图片描述

ZGC与其他垃圾搜集器比较

与CMS比较

CMS使用的是标记清除算法,会有内存碎片的问题,而ZGC使用的是标记整理算法,因此ZGC就没有内存碎片的问题。

CMS还有一个严重的问题,那就是如果在并发清理阶段用户线程创建对象的速度过快,导致内存被占满,此时CMS会停止用户线程(STW),改用Serial Old垃圾收集器进行内存收集,而Serial Old是单线程的,此时STW时间就非常长了。

与G1比较

G1最大的不如ZGC的地方,就是G1的第四个阶段筛选回收是需要STW的,因为G1使用的是标记整理算法,而标记整理算法是需要转移对象然后修正错误指针的,此时如果用户线程通过一个错误指针去寻址,就会发生异常,因此需要STW。而ZGC通过染色指针加读屏障加转发表,就解决了这个问题,使得ZGC在进行对象转移的时候,可以让用户线程继续运行,这样就大大缩短了STW的用户线程停顿时间。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第1章、react基础知识;

一、react学习前期准备; 1、基本概念; 前期的知识准备: 1.javascript、html、css; 2.构建工具:Webpack:https://yunp.top/init/p/v/1 3.安装node:npm:https://yunp.top/init/p/v/1 …

cmake制作并链接动静态库

cmake制作并链接动静态库 制作静态库add_library(库名称 STATIC 源文件1 [源文件2] ...)LIBRARY_OUTPUT_PATH指定库的生成路径 制作动态库add_library(库名称 SHARED 源文件1 [源文件2] ...) 连接动静态库link_libraries连接静态库link_directories到哪个路径去找库target_link…

UnityShader学习计划

1.安装ShaderlabVS,vs的语法提示 2. 常规颜色是fixed 3.FrameDebugger调试查看draw的某一帧的全部信息,能看到变量参数的值

雅马哈电钢琴YDP145

数据线:MIDI 琴可以通过MIDI、线直接连接手机,播放声音 琴通过线连接电脑,不能直接播放声音 https://www.bilibili.com/video/BV1ws4y1M7yw 操作: https://usa.yamaha.com/support/updates/yamaha_steinberg_usb_driver_for_win…

王道汽车4S企业管理系统 SQL注入漏洞复现

0x01 产品简介 王道汽车4S企业管理系统(以下简称“王道4S系统”)是一套专门为汽车销售和维修服务企业开发的管理软件。该系统是博士德软件公司集10余年汽车行业管理软件研发经验之大成,精心打造的最新一代汽车4S企业管理解决方案。 0x02 漏洞概述 王道汽车4S企业管理系统…

完美照片由构图决定,摄影构图基础到进阶

一、资料描述 本套摄影构图资料,大小1.04G,共有51个文件。 二、资料目录 新手必备-摄影构图技巧.doc 无忌版的《摄影构图学》.pdf 完美照片的十大经典拍摄技法.pdf 数码摄影曝光手边书.pdf 数码摄影不求人.30天学会数码摄影构图.pdf 数码单反摄影…

sheng的学习笔记-AI-决策树(Decision Tree)

AI目录:sheng的学习笔记-AI目录-CSDN博客 目录 什么是决策树 划分选择 信息增益 增益率 基尼指数 剪枝处理 预剪枝 后剪枝 连续值处理 另一个例子 基本步骤 排序 计算候选划分点集合 评估分割点 每个分割点都进行评估,找到最大信息增益的…

靠谱的大型相亲交友婚恋平台有哪些?相亲app软件前十名

靠谱交友软件,个人感觉还是要选择大型的,口碑好的进行选择,以下是我用过的婚恋平台,分享给大家 1、丛丛 这是我用的最久的一款脱单小程序,我老公就是在这个小程序找到的!!! 这是一款…

CSS边框

目录 内容区(content): 边框(border): 前言: 示例: 内容区(content): 内容区就是盒子里面用来存放东西的区域,里面你可以随便放如:…

计算机三级数据库技术备考笔记(十三)

第十三章 大规模数据库架构 分布式数据库 分布式数据库系统概述 分布式数据库系统是物理上分散、逻辑上集中的数据库系统。系统中的数据分布在物理位置不同的计算机上(通常称为场地、站点或结点,本章均用场地来描述),由通信网络将…

大语言模型总结整理(不定期更新)

《【快捷部署】016_Ollama(CPU only版)》 介绍了如何一键快捷部署Ollama,今天就来看一下受欢迎的模型。 模型简介gemmaGemma是由谷歌及其DeepMind团队开发的一个新的开放模型。参数:2B(1.6GB)、7B&#xff…

VMware安装Red Hat7.9

1、下载Red Hat Enterprise Linux7.9版本 【百度网盘下载】 链接:https://pan.baidu.com/s/1567NfZRF48PBXfUqxumvDA 提取码:bm7u 2、在虚拟机中创建Red Hat7.9 【点击创建虚拟机】 【自定义高级】 【选择光盘映像安装】 全名自定义即可 【虚拟机命…

Dual-AMN论文翻译

Boosting the Speed of Entity Alignment 10: Dual Attention Matching Network with Normalized Hard Sample Mining 将实体对齐速度提高 10 倍:具有归一化硬样本挖掘的双重注意力匹配网络 ABSTRACT 寻找多源知识图谱(KG)中的等效实体是知识图谱集成的关键步骤&…

分享 WebStorm 2024 激活的方案,支持JetBrains全家桶

大家好,欢迎来到金榜探云手! WebStorm公司简介 JetBrains 是一家专注于开发工具的软件公司,总部位于捷克。他们以提供强大的集成开发环境(IDE)而闻名,如 IntelliJ IDEA、PyCharm、和 WebStorm等。这些工具…

Zynq学习笔记--AXI 总线概述

目录 1. AXI总线概述 1.1 主要特点 1.2 通道功能 1.3 信号概览 2. AXI Interconnect 2.1 信号说明 2.2 内部结构 3. PS-PL AXI Interface 3.1 AXI FPD/LFP/ACP 3.2 Address Editor 3.3 地址空间 3.4 AXI-DDR 4. 通过ILA观察AXI信号 4.1 AXI 读通道 1. AXI总线概述…

故障诊断 | Matlab实现基于小波包结合鹈鹕算法优化卷积神经网络DWT-POA-CNN实现电缆故障诊断算法

故障诊断 | Matlab实现基于小波包结合鹈鹕算法优化卷积神经网络DWT-POA-CNN实现电缆故障诊断算法 目录 故障诊断 | Matlab实现基于小波包结合鹈鹕算法优化卷积神经网络DWT-POA-CNN实现电缆故障诊断算法分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现基于小波…

复杂DP算法(动态规划)

复杂DP算法 一、线性DP例题1、鸣人的影分身题目信息思路题解 2、糖果题目信息思路题解 二、区间DP例题密码脱落题目信息思路题解 三、树状DP例题生命之树题目信息思路题解 一、线性DP 例题 1、鸣人的影分身 题目信息 思路 题解 #include <bits/stdc.h> #define endl …

funasr 麦克风实时流语音识别

参考: https://github.com/alibaba-damo-academy/FunASR chunk_size 是用于流式传输延迟的配置。[0,10,5] 表示实时显示的粒度为 1060=600 毫秒,并且预测的向前信息为 560=300 毫秒。每个推理输入为 600 毫秒(采样点为 16000*0.6=960),输出为相应的文本。对于最后一个语音…

Thinkphp6接入PayPal支付

沙盒环境示例 创建扩展封装类 <?php namespace lib;class PayPalApi {//clientIdprivate $clientId;//clientSecretprivate $clientSecret;//服务器地址private $host https://api-m.sandbox.paypal.com/;//主机头private $headers [];//api凭证private $token ;//报文…

YOLO系列简记

本文主要参考了论文 A Comprehensive Review of YOLO Architectures in Computer Vision: From YOLOv1 to YOLOv8 and YOLO-NAS&#xff0c;以及其中提到的各 YOLO 原论文。 NMS 对所有检测框&#xff0c;按置信度降序排序。选择最高置信度的检测框&#xff0c;添加到最终结果…