JVM-G1垃圾回收器:G1回收流程(Rset、CSet、SATB)

  • G1垃圾回收的周期
  • App Thread (用户线程)
  • Concurrence Refinement Thread(同步优化线程)
  • RSet(Remember Set :记忆集合)
  • CSet(Collection Set 回收集合)
  • SATB(Snapshot At the Begging)
    • SATB + RSet 解决了什么问题?
  • YoungGC 的工作流程
  • MixGC 的工作流程
  • 转移失败的担保机制 Full GC
  • 总结一下

G1垃圾回收器总体来说,分为两种回收方式:第一种就是:YoungGC 对年轻代Region的回收,第二种就是MixGC。这种分代思想是垃圾回收器一直使用的。这种方式,让垃圾回收器只关注要回收的区域,降低程序的回收暂停时间。

G1垃圾回收的周期


从上图,我们可以看出来。G1垃圾回收器的垃圾回收方式分为两种。一就是YoungGC,主要回收的是年轻代Region中的垃圾。第二种就是混合回收模式(Mix GC),这种回收模式会回收年轻代跟老年代中的所有垃圾。根据上图,我们接下来就详细讲解一下垃圾回收周期中的各个流程的详细内容。

首先我们要知道的是G1垃圾回收器的算法是标记-整理算法,通过回收一个个Region(上一章我们讲到过的G1内存模型),来充分减少标记-整理算法上的内存浪费的缺点。

然后我们科普几个名词解释

App Thread (用户线程)

这个很简单,App thread 就是执行一个java程序的业务逻辑,实际运行的一些线程。

Concurrence Refinement Thread(同步优化线程)

这个线程主要用来处理代间引用之间的关系用的。当赋值语句发生后,G1通过Writer Barrier技术,跟G1自己的筛选算法,筛选出此次索引赋值是否是跨区(Region)之间的引用。如果是跨区索引赋值,在线程的内存缓冲区写一条log,一旦日志缓冲区写满,就重新起一块缓冲重新写,而原有的缓冲区则进入全局缓冲区。Concurrence Refinement Thread 扫描全局缓冲区的日志,根据日志更新各个区(Region)的RSet。这块逻辑跟后面讲到的SATB技术十分相似,但又不同SATB技术主要更新的是存活对象的位图。
Concurrence Refinement Thread(同步优化线程) 可通过 -XX:G1ConcRefinementThreads (默认等于-XX:ParellelGCThreads)设置。如果发现全局缓冲区日志积累较多,G1会调用更多的线程来出来缓冲区日志,甚至会调用App Thread 来处理,造成应用任务堵塞,所以必须要尽量避免这样的现象出现。可以通过阈值-X:G1ConcRefinementGreenZone/-XX:G1ConcRefinementYellowZone/-XX:G1ConcRefinementRedZone 这三个参数来设置G1调用线程的数量来处理全局缓存的积累的日志。

RSet(Remember Set :记忆集合)

每一个Region都会划出一部分内存用来储存记录其他Region对当前持有Rset Region中Card的引用,这个记录就叫做Remember Set。我们可以看看以下的分区模型图:

G1垃圾回收器,有对STW时间的控制,通过参数 -XX:MaxGCPauseMillis 来设置,而对于整个堆进行一次回收所需要的的实际STW时间可能远远超过这个值,所以G1可以不用扫描整个堆,只要通过扫描RSet来分析垃圾比例最高的Region区,放入CSet(Collection Set :回收集合)中,进行回收。

Rset的储存方状态,会根据对当前区域中引用数量的增加依次递增,分别为:稀疏(hash)->细粒度->粗粒度。

稀疏状态: 一个其他Region引用当前Region 中Card 的集合 被放在一个数组里面,Key:redion地址 Value:card 地址数组

细粒度: 一个Region地址链表,共同维护当前 Region 中所有card 的一个BitMap集合,该card 被引用了就设置对应bit 为1,并且还维护一个 对应Region对当前Region中card 索引数量

粗粒度: 所有region 形成一个 bitMap,如果有region 对当前 Region 有指针指向,就设置其对应的bit 为1

加入一条索引的源码的工作流程图如下:

我们发现如果有Rset的数据结构退化成了粗粒度的时候,要对Region进行回收的时候,就必须对Region进行全扫描才能正确回收,这样就大大增大了G1垃圾回收器的工作量,降低了效率。

其次为了追求效率一般Young代Region不会有RSet,因为维护Rset需要消耗不少性能,而年轻代快速回收的特性,带来了大量的浪费

有兴趣的同学可以去下载OpenJdk源码: https://hg.openjdk.java.net/jdk/jdk12,也可以看如下博客:https://blog.csdn.net/a860MHz/article/details/97276211

CSet(Collection Set 回收集合)

收集集合(CSet)代表每次GC暂停时回收的一系列目标分区。在任意一次收集暂停中,CSet所有分区都会被释放,内部存活的对象都会被转移到分配的空闲分区中。因此无论是年轻代收集,还是混合收集,工作的机制都是一致的。年轻代收集CSet只容纳年轻代分区,而混合收集会通过启发式算法,在老年代候选回收分区中,筛选出回收收益最高的分区添加到CSet中。

CSet根据两种不同的回收类型分为两种不同CSet。
1.CSet of Young Collection
2.CSet of Mix Collection
CSet of Young Collection 只专注回收 Young Region 跟 Survivor Region ,而CSet of Mix Collection 模式下的CSet 则会通过RSet计算Region中对象的活跃度,活跃度阈值-XX:G1MixedGCLiveThresholdPercent(默认85%),只有活跃度高于这个阈值的才会准入CSet,混合模式下CSet还可以通过XX:G1OldCSetRegionThresholdPercent(默认10%)设置,CSet跟整个堆的比例的数量上限。

SATB(Snapshot At the Begging)

SATB,也可以称为对象快照技术,在GC之前对整个堆进行一次对象索引关系,形成位图,相当于堆的逻辑快照。在并并发回收过程中,通过增量的方式维护这个对象位图。

  1. SATB解决了什么问题?
    主要是为了解决并发标记过程中,出现的漏标,误标等问题。

  2. 什么是漏标,误标。
    在并发标记过程中,APP线程跟GC线程同时进行,GC线程扫描的时候发现一个对象位垃圾对象,不对他进行标记,而APP线程马上就会操作索引指向这个对象。那么在垃圾回收的时候这样漏标的对象被回收就会产生灾难性的后果。也有的情况就是,在GC标记完一个对象不需要回收之后,APP线程之后就会把所有指向这个对象的索引完全去除,那么这就是一个垃圾对象,然而在回收过程之中并没有回收,造成了浮动垃圾,这种情况就是误标。

  3. 漏标、误标的解决方案。
    解决漏标误标,就必须了解两个名词。第一个名词:三色标记法,第二个名词:writer barrier(写栅栏)
    首先解释第一个名词:三色标记算法
    首先我们知道无论是 g1还是CMS垃圾标记算法都叫做根可达(root searching),首先搜索比如 线程栈上的对象、静态变量、常量池中的对象以及jni指针,这个部分往往发生是G1的初始标记阶段,会进行STW。然后就进入了并发标记阶段。首先我们定义:扫描过当前对象以及其子索引对象的为不可回收的对象位黑色对象,有黑色父对象索引指向的,并且未扫描其子索引的对象为灰色对象,需要回收的对象:为白色对象。

    第二个名词:写栅栏:write barrier
    当上图中 B->C 改变成 A->C 的索引,垃圾回收器是如何感知的?就是通过writer barrier技术,其实就相当于一个钩子程序,但执行索引改变的时候,触发一下write barrier,然后write barrier根据相应的需求增加一条索引更改的日志。每个App现场都会有一个LTB(local thread buffer)当一个LTB缓冲区写满之后,就新起一个缓冲区,把原来的缓冲区写入全局缓冲中,又相应的垃圾回收线程去更新SATB 的对象快照图。

SATB + RSet 解决了什么问题?

上面说了三色标记算法,为了解决漏标问题提出了一个writeBarrier的解决方案。但是还是有一种情况的漏标是writeBarrier解决不了的。就是在并发的情况下,当一个线程扫描对象A,对象A有索引:A->B,A->C,其中线程T1,扫描完B在扫描C的状态中,此时有个线程T2把索引B改动,改成A->D,把A设置为灰色,此时T1把C扫描完了,把A设置为黑色。这时我们就发现黑色对象A,下面就会有一个白色对象D未扫描。那么这样的漏标如何解决?
SATB位图构建过程中,所有有索引改动的对象,如上面所说的D跟B,就放入一个队列中。当Remark阶段,扫描这个队列里面的所有对象,重新标记。但是重新标记,按照道理来说,我们又需要扫描整个堆,但是我们其实只想回收某一个Region,又去扫描整个堆效率上来说肯定是不行的。这个时候,我们就可以去扫描Region中的RSet,如果RSet 没有记录其他Region对这个对象的索引,自己内部也没有,那么这个对象就是一个可回收的垃圾对象。

CMS中通过incremental update解决了部分漏标问题,但是像这样并发情况的下的漏标是不能解决的。所以为了解决可能存在的漏标问题,也是通过WriterBarrier,将A这样有改变过索引的对象放入一个堆栈中,在AbortPreClean、Remark阶段重新扫描一次这些对象。

YoungGC 的工作流程

YGC 的工作流程很简单:APP线程跑,然后就进行青年代Region的回收,把需要回收的YoungRegion,放入YoungCSet中,在YGC阶段就进行对年轻代CSet中的Region进行回收。因为大部分都是垃圾,且用了复制回收算法,基本只需要较短时间的STW就能完全回收了。

MixGC 的工作流程

当老年代垃圾达到一个阀值的时候,就会触发MixGC,就像第一幅图IPOH所标识的那样。阈值-XX:InitiatingHeapOccupancyPercent(默认45%)时,G1就会启动一次混合垃圾收集周期。在经过第一次YGC的同时,进行Init Mark,然后再后面几次YGC的整个过程中进行进行ConcurrentMark,并把需要收集的Region放入CSet当中。然后进行一次STW的时候,ReMark(重新标记,主要是扫描堆栈上新的对象的索引),并且进行Clean(主要的任务是直接清理没有用的大对象Region,也叫做HumongousRegion),然后就开始分步清理CSet中的Region,根据ReSet中计算出垃圾比率较高的Region开始清理。这一系列循环收集的过程称为混合收集周期(Mixed Collection Cycle)。

转移失败的担保机制 Full GC

我们看到第一幅回收流程图的过程,进行MixGC的同时也在并发的进行APP线程,产生了新的垃圾,如果这个时候发生了新产生的对象进入老年代Region,而堆空间不够的时候就会发生转移失败(Evacuation Failure),此时G1便会触发担保机制,执行一次STW式的、单线程的Full GC。Full GC会对整堆做标记清除和压缩,最后将只包含纯粹的存活对象。但是有一个参数-XX:G1ReservePercent(默认10%)可以保留空间,来应对晋升模式下的异常情况,最大占用整堆50%,但是太大了一般会浪费空间,也没有太大的意义。

G1在以下场景中会触发Full GC,同时会在日志中记录to-space-exhausted以及Evacuation Failure:

  • 从年轻代分区拷贝存活对象时,无法找到可用的空闲分区
  • 从老年代分区转移存活对象时,无法找到可用的空闲分区
  • 分配巨型对象时在老年代无法找到足够的连续分区

由于G1的应用场合往往堆内存都比较大,所以Full GC的收集代价非常昂贵,应该避免Full GC的发生。

总结一下

总的来说G1是一款非常优秀的垃圾回收器,现在也在企业应用的过程中越来越多的用到。其控制响应时间,跟简单的设置就能达到优秀的效率上面来说相对于于CMS等垃圾回收器来说无疑是巨大的进步。在我们发现使用CMS垃圾回收器时候了解不是很深刻,又或者对CMS调优方面已经在当前面临的情况没有好的办法了,尝试切换成G1垃圾回收器肯定会打来意想不到的收获。

但是在G1也有不少的缺点,因为有Concurrent Refine Thread 还有 RSet 等机制的存在一是对性能的影响,对内存的浪费都存在不少缺点。不过现在JDK15已经在2020.09.15面世,其中已经将ZGC(Zero GC作为主推的垃圾回收器),其管理内存在理论上已经达到了32T,而且MaxPauseTime 可以保证在10ms以内,无疑是巨大的进步。不过JDK15还不是LTS版本,所以只能说期待吧。有机会可以在以后的时间中与大家分享ZGC的内容。

JVM-G1垃圾回收器:G1回收流程(Rset、CSet、SATB)相关推荐

  1. java虚拟机收集器_Java虚拟机(JVM)垃圾回收器G1收集器 - Break易站

    G1收集器 G1(Garbage-First)是JDK7-u4才推出商用的收集器: 1.特点 (A).并行与并发 能充分利用多CPU.多核环境下的硬件优势: 可以并行来缩短"Stop The ...

  2. 双/三色标记法的垃圾回收(GC)原理解析和缺陷解决方案(Go,Lua以及jvm的CMS和G1垃圾回收器中使用的回收算法)

    标记-清除算法 go和lua虚拟机以及jvm的CMS和G1垃圾回收器的回收算法的思想均来自于标记-清除算法(Mark-Sweep),它们的gc有重要的两部分: 1.从根节点遍历所有对象,如果可达到,则 ...

  3. JVM垃圾回收器G1详解

    1.概述 在我们应用程序所应对的业务越来越庞大.复杂,用户越来越多,没有GC就不能保证应用程序正常进行,而经常造成STW的GC又跟不上实际的需求,我们需要不断地尝试对GC进行优化.G1(Garbage ...

  4. 029、JVM实战总结:大厂面试题:最新的G1垃圾回收器的工作原理,你能聊聊吗

    1.ParNew + CMS的组合让我们有哪些痛点? 痛点:STW,且停顿时间不可控 G1垃圾回收器比~更好的垃圾回收性能 2.G1垃圾回收器 G1 同时回收新生代和老年代的对象,把java堆拆分为多 ...

  5. jvm性能调优 - 18白话G1垃圾回收器的工作原理

    文章目录 ParNew + CMS的组合的痛点 G1垃圾回收器 G1是如何做到对垃圾回收导致的系统停顿可控的? Region可能属于新生代也可能属于老年代 总结 ParNew + CMS的组合的痛点 ...

  6. jvm(17)垃圾回收器

    垃圾回收器 GC分类与性能指标 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本. 从 ...

  7. JVM面试必问:G1垃圾回收器

    摘要:G1垃圾回收器是一款主要面向服务端应用的垃圾收集器. 本文分享自华为云社区<JVM面试高频考点:由浅入深带你了解G1垃圾回收器!!!>,原文作者:Code皮皮虾 . G1垃圾回收器介 ...

  8. 垃圾回收器之 G1 垃圾回收器

    4.4 G1 定义:Garbage First 2004论文发布 2009 JDK 6u14 体验 2012 JDK 7u4 官方支持 2019 JDK9 默认 (废弃了之前的 CMS 垃圾回收器) ...

  9. 031、jvm实战总结:动手实验:线上系统部署如果采用G1垃圾回收器,应该如何设置参数?

     1.前文回顾 1.G1中有新 .老.大三种Region 2.新生代回收条件:新生代Eden区满的时候 3.新生代GC仍然采用复制算法 4.控制停顿时间,对Region进行挑选回收 5.进入老年的条件 ...

  10. JVM七大垃圾回收器上篇Serial、ParNeW、Parallel Scavenge、 Serial Old、 Parallel Old、 CMS、 G1

    GC逻辑分类 垃圾收集器没有在规范中进行过多的规定,可以由不同的厂商.不同版本的JVM来实现. 由于JDK的版本处于高速迭代过程中,因此Java发展至今已经衍生了众多的GC版本. 从不同角度分析垃圾收 ...

最新文章

  1. Android基础教程(四)之-----取得手机屏幕大小DisplayMetrics的应用
  2. 浅谈JS的数组遍历方法
  3. MySQL函数和操作符(超详细,备着查找)
  4. php论坛思路,PHP论坛实现积分系统的思路代码详解
  5. Matlab程序仿真与调试入门
  6. ArcGIS 的 http://localhost:8399/arcgis/rest/services 无法打开,显示404 的解决办法
  7. The operation cannot be completed. See the details.
  8. 笔记-method-swizzling~那些年,一起遇过的坑
  9. CMD获取当前目录的绝对路径
  10. 机器学习算法篇:最大似然估计证明最小二乘法合理性
  11. mnist数据集无法下载
  12. 不要被后端糊弄了,我来告诉你网关是什么
  13. LiveData setValue和postValue的区别及详解
  14. HDU 4826Labyrinth(dp)
  15. MATLAB生成彩色图像,显示原始灰度与负片
  16. 开发必备的图片格式——.9图的原理和陷阱
  17. canvas绘制分享商品海报——已完成
  18. HTML——疑难杂症总结
  19. 巧用clang 的sanitize解决realloc,malloc,calloc失败
  20. 图片透明代码html5,javascript – 透明图像背景html5画布

热门文章

  1. python飞机大战实训报告200_飞机大战实训报告方案.doc
  2. JAVA实现汉字转换为拼音 自动识别常用多音字 JPinyin
  3. 微信屏蔽的是域名还是服务器ip,域名从未使用也会被微信屏蔽,这个你怎么看?...
  4. Microsemi Libero使用技巧1——查看芯片资源占用情况
  5. 数据建模:个人信用分是怎么计算出来的?
  6. OpenCV 表盘指针自动读数
  7. 指纹识别综述(4): 指纹匹配
  8. Win10的Git中文文件名繁体火星文乱码问题
  9. 普适计算-2014/04/04
  10. 可见光波长和颜色的对应关系