Garbage First(G1)垃圾回收过程

Posted by 李小武 on June 17, 2013

下面有两篇关于G1介绍的文章:

Garbage First(G1)介绍

Garbage First(G1) 日志分析工具

G1堆实现

图注:

灰色区域-空闲区域

蓝色区域-老年代(Old Generation)

绿色区域-新生代(Young Generation)

在G1中,堆被划分成 许多个连续的区域(region)。每个区域(region)的下大小相等,在1M~32M之间。JVM最多支持2000个区域,由此可以推算G1能支持的最大内存:2000*32M=62.5G。区域(region)的大小在JVM初始化的时候决定,也可以用-XX:G1HeapReginSize设置(不建议)。

在G1中没有物理上的Yong(Eden/Survivor)/Old Generation,它们是逻辑的,使用一些非连续的区域(Region)组成的。

Young GC

G1的新生代收集跟ParNew类似,当新生代占用达到一定比例的时候,开始出发收集。

回收过程:

被圈起的绿色部分为新生代的区域(region),这些区域(region)中的存活对象被复制到一个或者多个区域中,这些被填充的区域将是新的新生代;一些对象被复制到老年代的区域中,因为这些对象的年龄(逃逸过一次yong GC年龄增加1)已经达到某个阈值(ParNew默认15)。

回收过程是停顿的(STW,stop the word),也就是说,应用程序的线程被挂起,直到回收完成。回收完成之后,下次GC的Eden和Survivor大小被重新计算。根据yong GC的统计信息调整Eden和Survivor的大小,有助于合理利用内存,提高回收效率。

回收的过程也是并行的,多个回收线程并发收集。

回收结束:

深绿色和深灰色的区域为刚刚拷贝的对象。

Old GC

1)在标记阶段,首先是初始标记(Initial-Mark),这个阶段也是停顿的(stop-the-word),并且会稍带触发一次yong GC。对应GC log:GC pause (young) (inital-mark)

2)并发标记(Concurrent Marking)阶段,这个过程在整个堆中进行,并且和应用程序并发运行。并发标记过程可能被yong GC中断。在并发标记阶段,如果发现区域对象中的所有对象都是垃圾,那个这个区域会被立即回收(图中打X)。同时,并发标记过程中,每个区域的对象活性(区域中存活对象的比例)被计算。

3)再标记(Remark)阶段同样是停顿的(STW)。这个阶段是用来补充收集并发标记阶段产新的新垃圾(并发阶段和应用程序一同运行)。CMS中也有并非标记阶段,与之不同的是,G1中采用了更快的算法:snapshot-at-the-beginning (SATB)

4)清理阶段(Cleanup)选择活性低的区域(同时考虑停顿时间),等待下次yong GC一起收集,对应GC log: [GC pause (mixed)],这个过程也会有停顿(STW)。

5)回收/完成,新的yong GC清理被计算好的区域。但是有一些区域还是可能存在垃圾对象,可能是这些区域中对象活性较高,回收不划算,也肯能是为了迎合用户设置的时间,不得不舍弃一些区域的收集。

总结:

G1虽然保留了CMS关于代的概念,但是代已经不是物理上连续区域,而是一个逻辑的概念。在标记过程中,每个区域的对象活性都被计算,在回收时候,就可以根据用户设置的停顿时间,选择活性较低的区域收集,这样既能保证垃圾回收,又能保证停顿时间,而且也不会降低太多的吞吐量。Remark阶段新算法的运用,以及收集过程中的压缩,都弥补了CMS不足。引用Oracle官网的一句话:“G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS)”。

相信G1是未来!

参考资料:

http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html