HotSpot的G1收集器

G1收集器(Garbage-First)是在JDK7中正式使用的GC收集器。从长期看来,它是为了取代CMS收集器,并且在刚刚发布的JDK9中,G1已经成为默认的GC收集器

G1和其他收集器截然不同,从分代上看,它仍然属于分代垃圾收集器,也会区分新生代、老年代,仍然有Eden区和Survivor区;从堆结构上看,它并不要求新生代、老年代都连续。作为CMS的长期替代方案,G1使用了分区算法,其特点如下:

并行性:在回收期间,可以由多个GC线程同时工作,有效利用多核计算的能力。

并发性:拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行。一般来说,不会在整个回收期间完全阻塞应用。

分代收集:对比其他的收集器,它可以同时兼顾新生代和老年代。

空间整理:G1每次回收都会有效的复制对象,减少空间碎片。

可预见性:因为分区的原因,G1可以选择部分区域进行内存回收,减小了回收范围,对全局停顿也能得到较好的控制。

收集过程

G1的收集过程可能有4个阶段

1、新生代GC

2、并发标记周期

3、混合收集

4、可能会进行Full GC

新生代GC

新生代GC主要是回收Eden区和Survivor区。一旦Eden区被占满,新生代GC就会启动。

回收后,所有的Eden区都会被清空,Survivor区会被收集一部分数据,但是至少仍然会存在一个Survivor区。另外一个变化是老年代会增多。

并发标记周期

G1的并发阶段和CMS有些类似,他们都是为了降低一次停顿时间,从而可以和应用程序并发的部分单独提取出来执行。如下图所示:

初始标记:标记从GC Root直接可达的对象。会伴随一次新生代GC,并且产生全局停顿。

根区域扫描:初始标记后,Eden区被清空(伴随一次新生代GC),存活对象被移入Survivor区。在这一阶段,将扫描由Survivor区直接可达的老年代区域,并标记为可达对象。这个过程是并发执行的,但是不能和新生代GC同时进行。如果此时有新生代GC,则新生代GC会暂停,所以新生代GC的时间会变长。

并发标记:和CMS类似,并发标记会扫描堆内的所有存活对象,并做标记。这个过程有可能会被一次新生代GC打断。

重新标记:和CMS一样,重新标记会产生卡顿。因为并发标记过程会再次产生垃圾,因此重新标记是对上一步的修正补充。

独占清理:它会计算各个区域的存活对象和GC回收比例并进行排序,识别可提供混合回收的区域。

并发清理:识别并清理完全空闲的区域。

​​​​​​

上面两张图展示了并发标记周期前后的内存对比情况,其中有2点需要注意的是:1、收集完成之后还会存在Eden区(过程中有用户线程的执行);2、会产生一些G区域(由老年代标记产生),这些区域用于后面的混合收集

混合回收

在并发标记周期中,已经有部分对象被回收了,但是比例是比较低的。在上一阶段中,已经标记了G区域(包含较多的垃圾)。在混合阶段,就可以专门对这些区域进行回收,G1会优先收集垃圾比例较高的区域。

在这个阶段既会执行新生代GC,又会收集G区域(标记为垃圾的老年代区域),因此成为混合回收。需要注意的是:1、Eden区会被清空;2、G区域较高的会被收集(G里面存活的对象被移动到其他区域,这样可以减少内存碎片)。如下图所示:

​​​​​​

混合回收会执行多次,直到回收了足够的内存空间,然后它会触发一次新生代GC,然后进行后续的GC操作。整体过程如下所示:

​​​​​​​

必要时的Full GC

Full GC在内存不充足的情况下会产生。混合回收时内存不足,新生代GC时内存不足都会导致一次Full GC。


参考:《实战Java虚拟机》

代码段 小部件