Java 虚拟机 - 分代 GC


大多数 JVM 将堆分为三代——年轻代 (YG)、老年代 (OG) 和永久代(也称为终身代)。这种想法背后的原因是什么?

实证研究表明,大多数创建的对象的寿命都很短 -

实证研究

来源

https://www.oracle.com

正如您所看到的,随着时间的推移分配越来越多的对象,幸存的字节数变得越来越少(通常)。Java对象的死亡率很高。

我们将研究一个简单的例子。Java 中的 String 类是不可变的。这意味着每次需要更改 String 对象的内容时,都必须创建一个新对象。假设您在循环中对字符串进行了 1000 次更改,如下面的代码所示 -

String str = “G11 GC”;

for(int i = 0 ; i < 1000; i++) {
   str = str + String.valueOf(i);
}

在每次循环中,我们创建一个新的字符串对象,并且在上一次迭代期间创建的字符串变得无用(即,它没有被任何引用引用)。该对象的生命周期只是一次迭代 - 它们将立即被 GC 收集。这些短命对象被保存在堆的年轻代区域中。从年轻代收集对象的过程称为次要垃圾收集,它总是会导致“停止世界”暂停。

当年轻代被填满时,GC 会进行一次小型垃圾回收。死亡对象被丢弃,存活对象被移动到老年代。应用程序线程在此过程中停止。

在这里,我们可以看到这种一代设计所提供的优势。年轻一代只是堆的一小部分,很快就会被填满。但处理它所花费的时间比处理整个堆所花费的时间少得多。因此,在这种情况下,“停止世界”的停顿要短得多,尽管更频繁。我们应该始终以较短的停顿为目标,而不是较长的停顿,即使它们可能更频繁。我们将在本教程的后面部分详细讨论这一点。

年轻代分为两个空间——伊甸园和幸存者空间。在 eden 收集期间幸存的对象被移动到幸存者空间,而那些在幸存者空间中幸存下来的对象被移动到老年代。年轻一代在收集时会被压缩。

当对象被移动到老一代时,它最终会被填满,并且必须被收集和压缩。不同的算法对此采取不同的方法。其中一些会停止应用程序线程(这会导致长时间的“停止世界”暂停,因为老一代与年轻代相比相当大),而其中一些会在应用程序线程继续运行时同时执行此操作。这个过程称为full GC。CMS 和 G1是两个这样的收集器。

现在让我们详细分析这些算法。

串行GC

它是客户端类机器(单处理器机器或 32b JVM、Windows)上的默认 GC。通常,GC 是高度多线程的,但串行 GC 不是。它有一个线程来处理堆,并且每当它执行 Minor GC 或 Major GC 时,它就会停止应用程序线程。我们可以通过指定标志来命令 JVM 使用此 GC:-XX:+UseSerialGC。如果我们希望它使用某种不同的算法,请指定算法名称。请注意,老年代在主要 GC 期间被完全压缩。

气相色谱吞吐量

此 GC 在 64b JVM 和多 CPU 计算机上是默认的。与串行GC不同,它使用多个线程来处理年轻代和年老代。正因为如此,GC也被称为并行收集器。我们可以通过使用以下标志来命令 JVM 使用此收集器:-XX:+UseParallelOldGC-XX:+UseParallelGC(对于 JDK 8 及以上版本)。应用程序线程在执行主要或次要垃圾收集时停止。与串行收集器一样,它在主要 GC 期间完全压缩年轻代。

吞吐量GC收集YG和OG。当伊甸园填满时,收集器将其中的活动对象弹出到 OG 或幸存者空间之一(下图中的 SS0 和 SS1)。死去的物体被丢弃以释放它们所占据的空间。

YG GC之前

YG GC之前

YG GC后

YG GC后

在完整GC期间,吞吐量收集器清空整个YG、SS0和SS1。操作后,OG 仅包含活动对象。我们应该注意到,上述两个收集器在处理堆时都会停止应用程序线程。这意味着在主要 GC 期间会出现长时间的“stop-the-world”暂停。接下来的两种算法旨在消除它们,但代价是更多的硬件资源 -

内容管理系统收集器

它代表“并发标记-清除”。它的作用是使用一些后台线程定期扫描老年代并清除死对象。但在 Minor GC 期间,应用程序线程会停止。然而,停顿非常小。这使得 CMS 成为一个低暂停收集器。

该收集器需要额外的 CPU 时间来在运行应用程序线程时扫描堆。此外,后台线程只收集堆,不执行任何压缩。它们可能会导致堆变得碎片化。随着这种情况持续下去,在某个时间点之后,CMS 将停止所有应用程序线程并使用单个线程压缩堆。使用以下 JVM 参数告诉 JVM 使用 CMS 收集器 -

“XX:+UseConcMarkSweepGC -XX:+UseParNewGC”作为 JVM 参数告诉它使用 CMS 收集器。

气相色谱之前

气相色谱之前

气相色谱后

气相色谱后

请注意,收集是同时进行的。

G1GC

该算法的工作原理是将堆划分为多个区域。与 CMS 收集器类似,它在执行较小 GC 时会停止应用程序线程,并使用后台线程来处理老年代,同时保持应用程序线程继续运行。由于它将老一代划分为多个区域,因此在将对象从一个区域移动到另一个区域时,它会不断压缩它们。因此,碎片是最小的。您可以使用标志:XX:+UseG1GC告诉您的 JVM 使用此算法。与 CMS 一样,它也需要更多的 CPU 时间来处理堆并同时运行应用程序线程。

该算法设计用于处理较大的堆(> 4G),这些堆被划分为多个不同的区域。其中一些区域由年轻一代组成,其余区域由老一代组成。YG 的清除方式是传统的——所有应用程序线程都停止,并且所有在老一代或幸存者空间中仍然存活的对象。

请注意,所有 GC 算法都将堆分为 YG 和 OG,并使用 STWP 清除 YG。这个过程通常非常快。