12

Java 虚拟机原理 (五) G1垃圾收集器深入

 3 years ago
source link: https://blog.duval.top/2021/01/11/Java-%E8%99%9A%E6%8B%9F%E6%9C%BA%E5%8E%9F%E7%90%86-5-G1%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8%E6%B7%B1%E5%85%A5/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java 虚拟机原理 (五) G1垃圾收集器深入

发表于

2021-01-11 分类于 Java

阅读次数: 3 Valine: 0 本文字数: 3.4k 阅读时长 ≈ 3 分钟

深入了解G1垃圾收集器背后的核心原理。

CMS收集器使用了Incremental Update算法,而G1收集器使用的是SATB算法。这两者背后的思想都使用了三色标记算法,标记算法如下:

  • 黑色(black):自己已经被标记,且字段全部标记完毕的对象;
  • 灰色(gray): 自己已经被标记,但尚有字段未被标记的对象(collector正在访问的对象);
  • 白色(white):尚未标记对象。

注意: 标记结束后,被标记的对象是存活对象,没有被标记的对象会被回收。

在并行GC阶段,应用线程和GC形成并行,所以有可能同时发生以下两种情况:

  • 1.mutator把一个白对象的引用存到黑对象的字段里;
  • 2.某个白对象失去所有能从灰对象到达它的引用路径(直接或间接)。

黑对象持有了指向白对象的引用。根据定义,collector已经不会再去遍历黑对象的字段,所以发现不了这里还有一个活引用指向这个白对象。如果还有某个灰对象持有直接或间接引用能到达这个白对象,那就没关系;如果从灰对象出发的所有引用到这个白对象的路径都不幸被切断了,那这个白对象就要被漏扫描了。

所以如果同时发生以上两种情况,会导致对象漏标而被回收掉。

Incremental Update是CMS收集器针对上述问题而采用的算法。该算法的做法是:只要在write barrier里发现要有一个白对象的引用被赋值到一个黑对象的字段里,那就把这个白对象变成灰色的(例如说标记并压到marking stack上,或者是记录在类似mod-union table里)。这样就强力杜绝了上述第一种情况的发生。

而SATB的做法是把标记开始时的逻辑快照里所有的活对象都看作时活的。具体做法是在write barrier里把所有旧的引用所指向的对象都变成非白的(已经黑灰就不用管,还是白的就变成灰的)。

SATB(snapshot at the beginning)是G1并发理论的基础,用于维护确保回收过程的正确性。从名字上理解,就是GC在开始的时候先对活着的对象保存一个快照;

接下来,在GC过程中新分配的对象都是活的。

很容易知道哪些对象是一次GC开始之后新分配的:每个region记录着两个top-at-mark-start(TAMS)指针,分别为prevTAMS和nextTAMS。并发标记开始时候Top指针和NextTAMS指针是重合的(如下图所示),并发阶段分配的对象对位于[Next TAMS,Top]区间。这个区间内的对象默认都是存活的,这也叫做隐式标记。

g1gc-tams.png

g1gc-tams.png

但如果在Next TAMS之前有一个白色对象A被一个灰色对象B作为字段而引用,在并发标记扫描到这个字段之前,被赋值为null,那么B–>A的引用关系被切断,可能会导致白色对象A被漏标。

G1为了解决这个问题,在引用关系修改之前,插入一层pre-write barrier。pre-write barrier会把每次引用关系变化时旧的引用值记录下来。这些引用值会被放置到satb mark queue中,在下一次的并发标记阶段,会依次处理satb mark queue中的对象,确保这部分对象在本轮GC是存活的。

void G1SATBCardTableModRefBS::enqueue(oop pre_val) {
  // Nulls should have been already filtered.
  assert(pre_val->is_oop(true), "Error");

  if (!JavaThread::satb_mark_queue_set().is_active()) return;
  Thread* thr = Thread::current();
  if (thr->is_Java_thread()) {
    JavaThread* jt = (JavaThread*)thr;
    jt->satb_mark_queue().enqueue(pre_val);
  } else {
    MutexLockerEx x(Shared_SATB_Q_lock, Mutex::_no_safepoint_check_flag);
    JavaThread::satb_mark_queue_set().shared_satb_queue()->enqueue(pre_val);
  }
}

但这很可能有对象在snapshot中是活的,但随着并发GC的进行它可能本来已经死了,但SATB还是会让它活过这次GC。这就导致所谓的浮动垃圾。

该章节摘录自Java Hotspot G1 GC的一些关键技术

全称是Remembered Set,是辅助GC过程的一种结构,典型的空间换时间工具,和Card Table有些类似。还有一种数据结构也是辅助GC的:Collection Set(CSet),它记录了GC要收集的Region集合,集合里的Region可以是任意年代的。在GC的时候,对于old->young和old->old的跨代对象引用,只要扫描对应的CSet中的RSet即可。 逻辑上说每个Region都有一个RSet,RSet记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)。而Card Table则是一种points-out(我引用了谁的对象)的结构,每个Card 覆盖一定范围的Heap(一般为512Bytes)。G1的RSet是在Card Table的基础上实现的:每个Region会记录下别的Region有指向自己的指针,并标记这些指针分别在哪些Card的范围内。 这个RSet其实是一个Hash Table,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。

下图表示了RSet、Card和Region的关系(出处):

Remembered Sets.jpg

Remembered Sets.jpg

上图中有三个Region,每个Region被分成了多个Card,在不同Region中的Card会相互引用,Region1中的Card中的对象引用了Region2中的Card中的对象,蓝色实线表示的就是points-out的关系,而在Region2的RSet中,记录了Region1的Card,即红色虚线表示的关系,这就是points-into。 而维系RSet中的引用关系靠post-write barrier和Concurrent refinement threads来维护,操作伪代码如下(出处):

void oop_field_store(oop* field, oop new_value) {
  pre_write_barrier(field);             // pre-write barrier: for maintaining SATB invariant
  *field = new_value;                   // the actual store
  post_write_barrier(field, new_value); // post-write barrier: for tracking cross-region reference
}

post-write barrier记录了跨Region的引用更新,更新日志缓冲区则记录了那些包含更新引用的Cards。一旦缓冲区满了,Post-write barrier就停止服务了,会由Concurrent refinement threads处理这些缓冲区日志。 RSet究竟是怎么辅助GC的呢?在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。 而mixed gc的时候,old generation中记录了old->old的RSet,young->old的引用由扫描全部young generation region得到,这样也不用扫描全部old generation region。所以RSet的引入大大减少了GC的工作量。

学习资料


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK