GC算法之G1-PART III
G1 的设计目标之一是允许用户设置 GC 暂停时间目标。 G1 尝试通过自适应调整 Java 堆的大小来满足指定的暂停时间目标。它会根据暂停时间目标自动调整新生代的大小和总的 Java 堆大小。暂停时间目标越低,年轻代越小,总堆大小越大,老年代相对较大。
G1 的设计目标是将所需的调整限制为设置最大 Java 堆大小和指定 GC 暂停时间目标。
G1 将 Java 堆划分为很多个region。区域大小可以根据堆的大小而变化,但必须是 2 的幂,且至少为 1MB ,最多为 32MB。因此,可能的区域大小为 1、2、4、8、16 和 32MB。
区域大小计算基于初始和最大 Java 堆大小的平均值,如果初始和最大 Java 堆大小相距甚远,或者如果堆大小非常大,则可能有超过 2048 个区域。类似地,小的堆最终可能少于 2048 个区域。
每个区域都有一个关联的(remembered set)记忆集(包含指向该区域的指针的位置的集合,缩写为 RSet)。
在 JDK 8u40 中,某些巨大对象(humongous regions)的区域被收集为年轻代的一部分。
应用程序开始分配对象时,G1 选择一个可用区域,将其指定为eden区域,并开始将内存块从中分发给 Java 线程。一旦该区域已满,另一个未使用的区域将被指定为eden区域。该过程一直持续到达到eden区域的最大数量,此时启动Young GC。
当老年代空间的占用率达到或超过启动堆占用阈值(initiating heap occupancy threshold)时,G1发起老年代收集(个人认为这里更准确的说法是发起young gc + concurrent marking)。占用阈值由命令行选项 -XX:InitiatingHeapOccupancyPercent 控制,默认为 Java 堆的 45%。
当标记阶段显示老年代区域不包含活动对象时,G1 可以尽早回收老年代区域(个人提示,此时的回收发生在concurrent marking phase而不是mixed collection)。此类区域将添加到可用区域集中。包含活动对象的老年代区域将在未来的一次mixed collection中收集。