深入分析G1垃圾收集实现原理

1 与垃圾收集器有关的算法

​ 在分析G1前先简单回顾一下与垃圾收集器相关的算法。通常所谓的垃圾收集器更多地是指跟踪垃圾收集器(Tracing Garbage Collection),而不是引用计数(Reference Counting )垃圾收集器。跟踪垃圾收集器采用可达性分析方法确定哪些对象要被回收,通常会选取一些对象作为GC Roots,如果对象能直接或间接地被GC Roots中的对象引用,则认为该对象可达(存活对象)不能被回收,否则该对象不可达(垃圾对象)要被回收。

1.1 三色标记算法

​ 在确定内存中哪些对象是垃圾对象时,可以采用最简单的标记算法,即给内存中每个对象一个专门的标记位,被标记则认为是存活对象,否则是垃圾对象,然从GC Roots的对象集合开始递归遍历对象图,如果对象图中的对象能被GC Roots中的对象直接或简接引用则进行标记。理论上该算法可以确定内存中哪些对象是存活的,哪些对象是垃圾,但整个过程应用程序必须暂停,并且要处理所有的内存区域。

​ 为了解决上面的问题,Dijkstra等人在On-the-Fly Garbage Collection: An Exercise in Cooperation 一文中提出了三色标记(Tri-color Marking)算法。像Go、JavaScript 、Java等语言在内存回收上都采用了三色标记算法的变种。

​ **三色标记算法会创建白色、灰色、黑色三个集合,三个集合内分别只存储白色对象、灰色对象、黑色对象。白色对象,代表尚未开始标记的对象或已完成标记并确认为垃圾的对象;灰色集合,代表还在标记中的对象,即遍历对象图时已遍历到自己,但还未完成自己引用 对象的遍历;黑色对象,代表已完成标记并确认为存活的对象(正常情况下,对象标记的颜色变化只能白色变成灰色,灰色变成黑色)。起初黑色集合通常为空,灰色集合内为GC Roots直接引用的对象,其他对象均在白色集合内。一个对象任一时刻只能在白色、灰色、黑色三个集合中的某一个。**通常三色标记算法的处理流程如下:

1、起初除GC Roots 外的其他对象全白色集合,将GC Roots直接引用的对象从白色集合内移到灰色集合。

2、从灰色集合取出一个灰色对象,依次处理该对象引用的对象。若其未引用任何对象,则直接将其移入黑色集合中;若其引用的对象在白色集合中则将其移入灰色集合,否则直接不处理,当该灰色对象引用的对象全处理完后,再将其移入黑色集合中。

3、重复第2步的流程直到灰色集合为空。

4、上面的步骤处理完后,GC Roots与黑色集合内的对象为存活对象,而白色集合内的对象为垃圾对象,最后要做的就是将白色集合内的垃圾对象清理。

​ 下图展示了除GC Roots 另外有8个对象时,三色标记算法的处理流程。

  • 起初除了GC Roots内的对象外,其他对象全在右则的白色集合中。

  • 将GC Roots直接引用的对象从白色集合内移到灰色集合后,此时A对象与F对象已从白色集移到灰色集合。

  • 处理完灰色集合中的A对象引用的B对象后,此时B对象已从白色集移到灰色集合。

  • 处理完灰色集合中的A对象引用的C对象与D对象后。此时A对象已从灰色集合移到黑色集合,C对象与D对象已从白色集移到灰色集合。

  • 处理灰色集合中剩余的B对象、C对象、D对象与F对象后,B对象、C对象、D对象与F对象象已从灰色集移到黑色集合。

  • 经历过上面的处理后灰色集合已为空,三色标记法标记阶段结束到达清理阶段,白色集合中的E对象、G对象与H对象被清理,最后结果如下图。

1.2 三色标记算法的不足

​ 如果应用程序线程与三色标记算法的GC线程一起运行,则可能出现对象错标与漏标。所谓的对象错标是指原为是垃圾的对象被标记为黑色认为是存活的,这种情况的出现并不会引起应用程序的错误,只是会将垃圾收集的时间拖延到下一次垃圾回收。而对象漏标,则是原本要标记为黑色的对象,被遗漏了,没有被标记,最终导致该对象在白色集合中被垃圾回收集给回收掉;这种情况的一旦发生应用程序将出现未知的异常,这个异常可能是无关紧要的也可能是致命的。

​ 以上面的例子来看看漏标是怎么发生的。

假设GC线程准备下一步标记工作前,对象的标记状态如上图。此时GC线程下一步将处理灰色集合中的F对象,由于F对象未引用任何对象其将直接移动到黑色集合中,整体个灰色集合为空标记结束。可是如果在GC线程还未完成F对象从灰色集合转移到黑色集合的操作时,应用线程正好增加了F对象对G对象的引用呢?

​ 由于F对象已结束标记工作(实际GC线程已认为F对象是黑色的),F对象最终还是会从灰色集合成功地转移到黑色集合。而GC线程将无法感知应用程序新增加的F对象到G对象的引用,最终导致G对象的漏标。实际上产生漏标一定会满足下面两种情况的一种。

1、GC线程标记的过程中,应用线程增加黑色对象到白色对象的引用

2、GC线程标记的过程中,应用线程删除了灰色对象到白色对象的引用

上面的漏标示例实际是第一种情况,论文Uniprocessor Garbage Collection Techniques 的 3.2.1 Incremental approache小节将处理漏标时关注的点不同将GC分为 Snapshot-at-beginning collectors 与 Incremental update collectors。Snapshot-at-beginning collectors 关注于处理第一种情况,而Incremental update collectors关注于处理第二种情况,G1属于Snapshot-at-beginning collectors,而CMS属于Incremental update collectors。G1并发标记过程关注处理应用线程增加黑色对象到白色对象的引用,即当黑色对象新引用了白色对象时,便将这个黑色对象重新设置为灰(技术实现上采用pre-write barrier) ;而CMS发标记过程关注处理线程删除了灰色对象到白色对象的引用,即当灰色对象删除了白色对象的引用时,便将这个白色对象直接置灰(技术实现上采用post-write barrier)。

​ 那具体采用何种技术手段处理上面的两种情况呢?其实也很简单,就是想办法让GC线程感知对象引用的变化,即所谓的写屏障(write barrier)。这里的所说的写屏障并不是硬件层面的写屏障,而是软件层的写屏障,其实质可理解为在引用赋值这个写操作前加一个切面,根据切点加入时机不同又可分为 pre-write barrier 与post-write barrier,下面是G1中采用的写屏障的伪代码实现(来源于[HotSpot VM] 请教G1算法的原理 )。

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
}
复制代码

G1中利用pre-write barrier来保证并发标记过程中要处理的SATB(snapshot-at-the-beginning)的完整性(G1 SATB具体如何的实现后面会详细分析),即GC线程在跟踪标记开始阶段生成的对象图快照时,应用线程对该对象图快照的修改能通过pre-write barrier感知。另一方面G1采用post-write barrier来维护并发标记过程中应用线程新产生的需要跟踪的跨区间引用(后面分析G1的RSet时会再补充说明)。

1.3 对象的清除实现方式

​ 当对象标记结束后,便可以清除对象。而在具体实现时可以采用三种方式标记清除、标记复制、标记压缩算法。标记清除最为简单与高效,其直接将那些垃圾对象清除,但这也带来了内存碎片的问题,同时对象分配时也不得不采用空闲空间列表算法而不能采用高效的指针碰撞算法。标记复制算法通常要额外占用50%的空间,其实现是一直用一半内存存储对象,而另一半内存置空,当回收垃圾时,将已使用空间中仍存活的对象直接复制到置空的那段内存中,然后直接置空之前使用的那半内存。标记压缩算法,兼顾标记清除与标记复制算法的优点,在回收垃圾后会对存活对象进行相应的移动,尽量将碎片化的内存空间进行压缩。

2 分代垃圾收集器

​ 在分析G1垃圾收集器之前有必要先简单回顾一下HotSpot VM中的其他垃圾收集器。在G1出现之前HotSpot VM中的其他垃圾收集器都是基于新生

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值