Panda 白话 - G1垃圾收集器 之 RSet(Remembed Set)源码解读

本文详细介绍了G1垃圾收集器中的RSet(Remembered Set)原理,重点解析了RSet如何记录老年代到老年代以及老年代到年轻代的引用关系,探讨了点在(Point In)方式以及稀疏哈希表、细粒度和粗粒度位图等不同结构的使用场景。通过实例和图表帮助理解RSet在不同引用情况下的工作方式,并分析了源码中的数据迁移过程。
摘要由CSDN通过智能技术生成

G1的知识点越看越多,这个RSet和卡表老也整不明白,单拎出来白话一下吧~

我们已经知道G1将堆内存划分为2048个(默认、可调整)大小相等的Region,新创建的对象都是放在新生代Eden区。

Region分为5中类型:

  • YHR - Yound Heap Reagion :年轻代分区
    • ERH - Eden Heap Region : eden区,伊甸园,放新创建对象
    • SRH - Survivor Heap Region : Survivor 区,存货去,放每次GC后存活对象
  • OHR - Old Heap Region : 老年代分区,放长命对象
  • HHR - Humongous Heap Region : 巨型对象分区,存放巨大(>Region Size 的 50%)对象
  • FHR - Free Heap Region :空闲分区,还未进行分配

RSet - Remembed Set :记忆集

记忆集嘛,记得是什么呢?记录Region 间的引用关系,

Region间引用关系分为5种:

  • Region内部引用 : 存放再一个Region内的对象间引用 - 不需要记录
  • Young Region -> Young Region:年轻代引用年轻代 - 不需要记录
  • Young Region -> Old Region: 年轻代引用老年代 - 不需要记录
  • Old Region -> Young Region:老年代引用年轻代 - 需要记录
  • Old Region -> Old Region: 老年代引用老年代 - 需要记录

可以看到G1垃圾收集器做了三重过滤
我们来看一下为啥前三个不需要记录:

RH  ->  RH - GC是以Region为最小单位,回收时会扫描整个Region,
			 也就是Region内每个对象都会扫描到,谁引用谁自然知道了,无需额外空间记录
			 在处理RSet时过滤
			 
YRH -> YRH - G1提供了三种GC-YGC、Mixed GC、Full GC,每种GC都会全量回收新生代Region,
		     新生代中每个Region都会被整个扫描,代间引用就不用记了
		     在写屏障时过滤
		     
YRH -> ORH - YGC:只回收新生代,与老年代无关,不用记;
			 Mixed GC : 以新生代为根,根可达分析的时候就找到老生代了,不用记
			 Full GC : 整堆回收,还记啥了
			 在写屏障时过滤

引用的概念:

刚开始刷G1的时候看到RSet是一脸懵的,一时间反应不过来代间引用是啥东西,给同样发懵的你点一手,嘎嘎~

public class Panda {
    private Kongfu kongfu;

    public static void main(String[] args) {
        Panda panda = new Panda();
        Kongfu kongfu = new Kongfu();
        panda.kongfu = kongfu;
    }
}
class Kongfu {}

上面代码不要太简单了,那我们来解析一下,<{=....(嘎嘎~)

  • 有一个类Panda,Panda类有个属性是Kongfu类型的
  • 有个类Kongfu
  • 有个main方法,创建了Panda对象、Kongfu对象,将Kongfu对象的引用赋值给Panda对象的kongfu属性
    此时Panda对象就有一个到Kongfu对象的引用了,以上解析简直是听君一席话如听一席话,那我们再来图解一下,嘎嘎~
    在这里插入图片描述

上面是以堆得维度的图示,我们再来看下Region维度的引用关系,

在这里插入图片描述
上图示意:

  • 两个年轻代Region:YHR1-有两个对象Obj1、Obj2; YHR2 -有一个对象Obj1
  • 两个老年代Region:OHR1 - 有一个对象Obj2、OHR2 - 有一个对象Obj1
  • Obj1_YHR1.field1 = obj1_YHR2 - 年轻代YHR1 中 Obj1 对象 中 field1属性引用 年轻代YHR2中Obj1对象
  • Obj1_YHR1.field2 = obj2_OHR1 - 年轻代YHR1 中 Obj1 对象 中 field2属性引用 老年代代OHR1中Obj2对象
  • Obj1_OHR2.field1 = obj2_OHR1 - 老年代OHR1 中 Obj1 对象 中 field1属性引用 老年代OHR1中Obj2对象
  • Obj2_OHR1.field1 = obj2_YHR1 - 老年代OHR1 中 Obj2 对象 中 field1属性引用 年轻代YHR1中Obj2对象
  • Obj2_OHR1.field2 = obj1_YHR2 - 老年代OHR1 中 Obj2 对象 中 field2属性引用 年轻代YHR2中Obj1对象
  • Obj2_OHR1.field3 = obj1_OHR2 - 老年代OHR1 中 Obj2 对象 中 field3属性引用 老年代OHR2中Obj1对象
    还是看图说话吧:
    在这里插入图片描述
    现在已经知道啦引用的概念,代间引用的概念~
    下面我们来康康RSet是怎么记录这种引用关系的呢~

RSet 采用的是Point In的方式记录引用,Point In就是向里指嘛,就是谁指向了我,如上面的代码小例,Panda对象引用Kongfu对象,那么Kongfu对象所在的Region的RSet就会这个引用关系
Point In 方式有个问题,就是谁都可以引用我,如果我火了,指向我的对象越来越多,每个引用记录都得记啊,那我的RSet不爆表了吗,这个时候记录引用的额外空间的开销就太大了,

G1将RSet数据结构设置成动态变化的,有三种结构:

  1. 稀疏哈希表 - SparsePRT
  2. 细粒度 - PerRegionTable
  3. 粗粒度位图 - BitMap

RSet 数据结构-源码:HeapRegionRemSet.hpp

class OtherRegionsTable VALUE_OBJ_CLASS_SPEC {
   
	BitMap      _coarse_map;//粗粒度位图
	PerRegionTable** _fine_grain_regions;//细粒度PRT
	SparsePRT   _sparse_table;//稀疏哈希表
}

先补充个知识,打个提前量~
让我们先来康康卡表是啥子~~

Card Table - 卡表:

卡表就是将Region进一步划分,将Region划分为若干个物理连续的512Byte的card page - 卡页,这样每个Region就有一个卡表来映射Regin中的卡页,整堆有个global card table - 全局卡表 存储所有Region的卡表情况
这个老哥的图画的很明白,盗图如下:Heap -> Regions组成 -> Card Pages 组成

老哥的例子是:

  • Heap - 堆 大小设置为 1GB
  • 那么global card table - 全局卡表 的长度就是 1GB / 512Byte = 2097151 个
  • HR - Heap Region - 每个Region大小设置为 1MB
  • 那么每个Region 的卡表长度就是 1MB / 512Byte = 2048 个
  • 没毛病~
    在这里插入图片描述

回到正文,康康RSet都存储了啥子~

哈希表:默认长度是4

 key  - region address  引用对象所在Region的地址
 value - card page index array - 引用对象所在Region中卡表索引值数组,

key - 存储到Region级别的,我得知道哪个Region引用了我啊
value - card 级别的,我得知道引用我得对象在Region中具体那个位置啊
没毛病啊~

结构定义-源码:SparsePRTEntry


                
  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值