G1 Java垃圾回收过程 rset card table

关于一些基础的概念就不介绍了,可以参看这个:https://blog.csdn.net/waltonhuang/article/details/105541242

 

下面是关于通过这个视频进一步理解GC的过程:https://youtu.be/Gee7QfoY8ys

RSet 和 Card Table

下面这个图画的特别好,结构可以看得比较清楚各种结构和关系,包括RSet和Cardtable

G1 Java垃圾回收过程 rset card table

每个region都有一个Rset,记录了哪些region的哪些对象引用了本region的对象。

如图所示,站在EDEN region的角度,Old Region的B对象引用了我的E对象,C对象引用了我的D对象,所以在我的Rset里保存了一个数据结构,通过这个数据结构,我能找到B和D对象

结合Oracle官网以及这个源码解读blog的相关的概念。我们可以看到RSet里保存的这个数据结构也很有意思:因为RSet要保存其他对象对本region的引用,这个”其他对象“有可能多(好多对象都引用我,稠密)有可能少(没几个对象引用我,稀疏),G1在数据量不同的时候采用不同的数据结构。

最稀疏:

最稠密:"_coarse_map"(粗图)是一个位图,每个bit是一个region,这个bit是1说明了相应的region包含指针指向当前region

"_fine_grain_entries"(细颗粒)是哈希表,里面的元素是PerRegionTables(PRTs),说明regions

 

  • 我觉得Card Table是一个位图,全局只有一个,将整个heap的地址分成了若干份(符合图中红色画的一个地址范围)
  • 位图的第i位为1,代表整个heap的第i个地址范围dirty了(有新的从old到young的引用)
  • 然后每个region的RSet实际上只需要保存这个范围索引就行了。
  • 比如,B和C所在的内存在heap地址的第i份,那么EDEN region的RSet实际上只需要保存”i“这个数字,就已经可以找到B和C了。

Write barrier

G1 Java垃圾回收过程 rset card table

这是一小段代码,JVM注入到我们写的代码中,每次我们写一个赋值操作,比如object.filed = <reference>,都会运行这一小段代码。有点像mysql的binlog记录写操作

  • 这些操作记录会保存在card中
  • card保存在队列中(dirty card queue)这个跟card table应该不是一回事。这就相当于一个标记说,这块区域dirty了。
  • 队列分为4个区域,白,绿,黄,红。

写操作越来越多,这个队列就越来越长。

  • 写操作排队到白色区域,nothing happens
  • 写操作继续增长,排队到绿色区域,g1启动refinement 线程,这个线程会说,我知道这块区域被修改了,我需要更新Remember Set(为什么不立即更新Remember Set而要搞这么复杂?因为如果每次都更新Remember Set,会比较消耗资源,而且如果好多线程都想去写这个Remember Set,可能还会有竞争的问题。g1的做法是用队列保存这些操作,g1是唯一一个写这个数据结构的)
  • 到黄色区域,refinement线程增多
  • 到红色区域,g1就不再让排队了,而是让Application来帮忙,让Application运行另一段代码,这样Application运行慢了,写队列也变慢了,g1就能catch up消费这个队列了。

 

G1 Young GC Phase

StopTheWorld

造一个CollectionSet,在Young GC,这里面只有Eden region和Survivor region

1.Root Scanning

扫描staic和每个线程栈里的local对象

2.更新RS

清空dirty card queue,更新RS

3.处理RS

检测被Old对象指向的Eden对象

4.对象拷贝

5.reference processing

 

G1 Old GC

heap涨到一定程度了(45%)