📜  Java虚拟机-世代GC

📅  最后修改于: 2020-11-13 05:37:42             🧑  作者: Mango


大多数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);
}

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

随着年轻一代的忙碌,GC会进行少量垃圾收集。废弃的对象将被丢弃,而有生命的对象将被移交给旧的一代。应用程序线程在此过程中停止。

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

年轻一代分为两个空间-伊甸园和幸存者空间。在收集伊甸园后幸存的物体被移至幸存者空间,而在幸存者空间中幸存的物体则被移交给了老一代。年轻的一代在收集时被压紧。

随着对象移交给旧的一代,它最终会填满,必须进行收集和压缩。不同的算法对此采取不同的方法。他们中的一些人停止了应用程序线程(由于老一辈比年轻一代要大得多,这导致了较长的“世界停止”暂停),而其中一些则在应用程序线程保持运行的同时进行。此过程称为完整GC。 CMS和G1是两个这样的收集器。

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

串行GC

它是客户端级计算机(单处理器计算机或32b JVM,Windows)上的默认GC。通常,GC具有大量的多线程功能,而串行GC则不是。它只有一个线程来处理堆,并且在执行次要GC或主要GC时将停止应用程序线程。我们可以通过指定标志-XX:+ UseSerialGC来命令JVM使用该GC。如果我们希望它使用其他算法,请指定算法名称。请注意,在大型GC中,旧一代已完全压缩。

吞吐量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中,“ stoptheworld”会长时间停顿。接下来的两种算法旨在消除它们,但要消耗更多的硬件资源-

CMS收集器

它代表“并发标记扫描”。它的函数是它使用一些后台线程来定期扫描旧版本,并清除死对象。但是在次要GC期间,应用程序线程将停止。但是,停顿很小。这使得CMS成为低中断的收集器。

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

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

GC之前

GC之前

GC之后

GC之后

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

G1 GC

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

该算法已设计为处理更大的堆(> 4G),这些堆被划分为多个不同的区域。这些地区中有一些是年轻一代,其余则是老一代。传统上使用YG清除–停止所有应用程序线程,并且所有仍旧存在于旧世代或幸存者空间中的对象。

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