GC算法之G1-PART II
G1 老年代收集不需要收集整个老年代以释放老年代中的空间。相反,在任何时候只能收集老年代区域的一个子集。此外,这个老年代区域的子集是与新生代区域一起收集的。
描述老年代区域子集与年轻代集合的集合的术语是mixed GC。因此,mixed GC 是一种 GC 事件,其中除了老年代区域的子集外,还收集了所有年轻代区域。换句话说,mixed GC 是正在收集的年轻代和老年代区域的混合。
当超过 Java 堆占用阈值(heap occupancy threshold)时,G1 启动老年代收集。值得注意的是,G1 中的堆占用阈值衡量的是与整个 Java 堆相比的老年代占用率(old generation size/heap size)。熟悉 CMS GC 的读者记得,CMS 使用仅适用于老年代空间的占用阈值来启动老年代收集。在 G1 中,一旦达到或超过堆占用阈值,就会安排一个并行的 stop-the-world initial-mark 阶段执行。
初始标记阶段(initial-mark phase)与下一次年轻 GC 同时执行。一旦初始标记阶段完成,就会启动并发多线程标记阶段(concurrent multithreaded marking phase),以标记老年代中的所有活动对象。当并发标记阶段完成时,将安排一个并行的 stop-the-world 重新标记阶段(remark phase),以标记由于应用程序线程与标记阶段并发执行而可能丢失的任何对象。在 remark 阶段结束时,G1 拥有关于老年代区域的完整标记信息。如果碰巧有老年代区域中没有任何活动对象,则可以在并发周期的下一阶段(**清理阶段)(cleanup phase)**回收它们,而无需任何额外的 GC 工作。
在remark phase结束时,G1 可以识别一组最佳的老年代来收集。
在垃圾收集期间需要收集的区域(the set of regions)集和称为收集集 (CSet)
包含在 CSet 中的区域的选择基于可以释放多少空间和 G1 暂停时间目标。在 CSet 被识别后,G1 在接下来的几次年轻代 GC 中安排一次 GC 收集 CSet 中的区域。也就是说,在接下来的几次年轻代中,除了年轻代之外,还会收集一部分老年代。这就是前面提到的mixed GC类型的垃圾回收事件。
使用 G1,每个被垃圾收集的区域,无论是年轻代还是老年代,都会将其活动对象疏散到可用区域。一旦活动对象被疏散,已收集的年轻和/或旧区域将成为可用区域。
将活动对象从老年代区域疏散到一个可用区域的一个极大的好处是撤离的对象最终在虚拟地址空间中彼此相邻。 对象之间没有零散的空白空间。 实际上,G1 对老年代进行部分压缩。 请记住,CMS、并行和 Serial GC 都需要一个 full GC 来压缩老年代,那个压缩会扫描整个老年代。