Poison

G1 GC

在 Orcale 的官方网站中,对于 G1 GC 的介绍可以参见 Garbage-First Garbage CollectorGarbage-First Garbage Collector Tuning,其中关键点如下:

G1 收集器是一个服务端的垃圾收集器,适用于具有大内存的多处理器机器。它极有可能满足垃圾回收(GC)暂停时间目标,同时实现高吞吐量。整堆操作(例如全局标记)与应用程序线程同时执行。这样可以防止与堆或活动数据大小成比例的中断。

G1 收集器通过多种技术实现了高性能和暂停时间目标。

堆被划分为一组大小相等的 region,region 大小可以从 1 MiB 到 32 MiB 不等,具体取决于堆大小。目标是不超过 2048 个 region。Eden、Survivor、Old 是这些 region 中的逻辑分组,它们的分布并不连续。G1 执行并发全局标记阶段,以确定整个堆中对象的活动性。标记阶段完成后,G1 知道哪些 region 大部分为空。 它首先对这些 region 进行收集,通常会产生大量的空闲空间。这就是为什么这种垃圾收集方法称为 “垃圾优先” 的原因。 顾名思义,G1 将其收集和压缩活动集中在可能充满可回收对象(即垃圾)的 region 中。G1使用暂停预测模型来满足用户定义的暂停时间目标,并根据指定的暂停时间目标选择要收集的 region 数量。

由 G1 标识为可回收的成熟 region 是即将被收集的垃圾。G1 将对象从堆的一个或多个 region 复制到堆上的单个 region,并且在此过程中,压缩和释放了内存。垃圾收集是在多处理器上并行执行的,以减少暂停时间并增加吞吐量。因此,对于每次垃圾收集,G1 都在用户定义的暂停时间内连续工作以减少碎片。这超出了之前两种 GC 方式的能力。CMS(并发标记扫描)垃圾回收不会进行压缩。ParallelOld 垃圾收集仅执行整个堆压缩,这导致相当长的暂停时间。

G1 具有尝试达到的暂停时间目标(软实时)。在 Young GC 中,G1 会调整其 Young generation(Eden 和 Survivor),以达到软实时目标。在混合收集期间,G1 根据以下因素来调整对老年代的收集数量:混合垃圾收集的目标数量、堆内每个 region 活跃对象的百分比、总体可接受的堆浪费百分比。

G1 通过将活动对象从一个或多个 region 集(称为 Collection Sets (CSets))增量并行复制到不同的新的 region 中来实现压缩,从而减少了堆碎片。目标是从包含最大可回收空间的那些 region 开始,尽可能多地回收堆空间,同时尝试不超过暂停时间目标 (garbage first)。

G1 使用独立的 Remembered Sets (RSets) 来追踪对 region 的引用,独立的 RSet 支持并行和独立地对各个 region 进行收集,因为对单个 region 来说,仅仅只有一个 RSet 必须被扫描,而不是整个堆。G1 使用写后屏障来记录对堆的修改并更新 RSet。

除了构成 stop-the-world 的 Young GC 和 Mixed GC 外,G1 还拥有并行、并发和多阶段标记周期。

Young GC: G1 会收集 Eden 及 Survivor 区,从这些 region 中存活下来的对象将被拷贝或疏散至新的 region。特定对象的目标 region 取决于对象的年龄,年龄达到一定阈值的对象将被疏散到老年代(即被晋升),否则将被疏散至 Survivor region 并将被包含在下一次 Young GC 或 Mixed GC 时的 CSet 中。

Mixed GC: 成功完成并发标记周期后,G1 从执行 Young GC 切换为执行 Mixed GC。在 Mixed GC 中,G1 选择性的添加一些老年代 region 至 Eden 和 Survivor 组成的 region 中以被收集。添加的老年的 region 的数量由许多标志控制。在 G1 收集足够数量的老年代 region 后(在多次 Mixed GC 后),G1 调整回执行 Young GC 直到下一次标记周期完成。

在 G1 中,任何大于 region 大小一半的对象都被视为 Humongous Object,这样的对象直接分配到老年代的 “Humongous regions” 中。这些 “Humongous regions” 是一组连续的 region。

在分配任何 Humongous region 之前,将检查标记阈值,并在必要时启动并发标记。

死亡的 Humongous Object 在标记周期的清理阶段或 Full GC 时被回收。

为了减少复制开销,任何疏散暂停中均不包括 Humongous Object。Full GC 将对 Humongous Object 进行压缩。

如果你看到连续的由 Humongous 申请触发的并发标记并且这些申请使你的老年代碎片化,请增大你的 -XX:G1HeapRegionSize 以使这些之前的 Humongous Object 不再被判定为 Humongous Object 以遵循常规的分配路径。

G1 的首要目标是为运行需要大堆且对 GC 延迟时间限制的应用程序的用户提供解决方案。这意味着堆大小约为 6GB 或更大,并且稳定且可预测的暂停时间低于 0.5 秒。

如果当前具有 CMS 或 ParallelOld 垃圾收集器运行的应用程序具有以下一个或多个特征,则将其切换到 G1 将非常有益。

  • 超过 50% 的 Java 堆被实时数据占用。
  • 对象分配率或提升率差异很大。
  • 不必要的长时间垃圾收集或压缩暂停(长于 0.5 到 1 秒)

G1 被规划作为并发标记扫描收集器(CMS)的长期替代产品。将 G1 与 CMS 进行比较,存在一些差异,这些差异使 G1 成为更好的解决方案。一个区别是 G1 是压缩收集器。G1 足够紧凑,可以完全避免使用细粒度的空闲列表进行分配,而是依赖于 region。这大大简化了收集器的各个部分,并消除了潜在的碎片问题。此外,G1 提供的垃圾收集暂停比 CMS 收集器更具可预测性,并允许用户指定所需的暂停目标。

Reference

Garbage-First Garbage Collector Tuning(JDK8)
Garbage-First Garbage Collector(JDK9)
Garbage-First Garbage Collector Tuning(JDK9)
G1: One Garbage Collector To Rule Them All
Tips for Tuning the Garbage First Garbage Collector
JEP 156: G1 GC: Reduce need for full GCs
JEP 248: Make G1 the Default Garbage Collector
JVM G1GC 小册子
Plumbr Handbook Java Garbage Collection.pdf