JDK8垃圾回收调优指南--(6)并行收集器

原文:Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide--The Parallel Collector。

并行收集器(也称为吞吐量收集器),与串行收集器类似都是分代收集器;主要的区别是使用多个线程来加速垃圾回收。
使用命令行选项'-XX:+UseParallelGC'启用并行收集器。默认情况下,使用此选项,'minor collection'和'major collection'都会并行执行,以进一步减少垃圾收集开销。

在拥有N(N>8)个硬件线程的机器上,并行收集器使用固定比例的N值作为垃圾收集器线程的数量。对于N值较大的情况,比例约为5/8。当N值低于8时,使用的数值为N。在选定的平台上,比例下降为5/16。可以使用命令行选项(稍后将对此进行描述)调整垃圾收集线程的特定数量。在只有一个处理器的主机上,由于并行执行(例如同步,线程间通信)所需的开销,并行收集器的性能可能不如串行收集器。但是,当运行具有中型到大型堆的应用程序时,在具有两个处理器的机器上,它的性能通常要比串行收集器好一些,而且当有两个以上可用处理器时,它的性能通常要比串行收集器更好。

可以使用命令行选项'-XX:ParallelGCThreads='控制垃圾收集线程的数量。并行收集器和串行收集器一样,使用命令行对堆值进行调节,以获取好的性能。但是,启用并行收集器应该会缩短GC停顿时间。由于多个垃圾收集线程参与'minor collection',因此在GC期间,可能会由于从年轻代提升到老年代,出现一些碎片。'minor collection'涉及的每个垃圾收集线程都保留了部分老年代用于升级,将可用空间划分到这些“升级缓冲区”可能会导致碎片效应。减少垃圾收集线程的数量,增加老年代的大小可以减少这种碎片效应。

Generations

正如前面提到的,在并行收集器中,代的排列是不同的(相比于串行收集器)。这种排列如图6-1所示:

JDK8垃圾回收调优指南--(6)并行收集器_第1张图片

Parallel Collector Ergonomics

在服务器类机器上,默认情况下选择并行收集器。此外,并行收集器使用一种自动调优方法,允许您指定特定的行为,而不是代的大小和其他更低层级的调优细节。您可以指定最大GC停顿时间、吞吐量和占用空间(堆大小)。

  • 最大GC停顿时间:最大GC停顿时间目标由命令行选项'-XX:MaxGCPauseMillis='指定。这被解释为需要毫秒或更少的暂停时间;默认情况下,没有最大GC停顿时间目标。如果指定了GC停顿时间目标,则(JVM自动)调整堆大小和其他与垃圾收集相关的参数,以使GC停顿时间短于指定的值。这些调整可能会导致垃圾收集器降低应用程序的总体吞吐量,并且不能总是满足预期的GC停顿时间目标。
  • 吞吐量:吞吐量目标是根据执行垃圾收集花费的时间与垃圾收集之外花费的时间(称为应用程序时间)来计算的。目标由命令行选项'-XX:GCTimeRatio='指定,该选项将垃圾收集时间与应用程序时间的比率设置为1/(1+)。例如:'-XX:GCTimeRatio=19'设置的目标是总时间的1/20或5%用来GC。默认值为99,因此垃圾收集的时间目标为1%。
  • 占用空间(堆大小):使用选项'-Xmx'指定最大堆占用空间。此外,收集器有一个隐式的目标,即只要满足了其他目标,就最小化堆的大小。

Priority of Goals

这些目标的处理顺序如下:

  1. 最大GC停顿时间
  2. 吞吐量
  3. 最小内存占用

首先满足最大GC停顿时间目标。只有在达到这个目标之后,才会处理吞吐量目标。同样,只有在满足了前两个目标之后,才会考虑(内存)空间占用目标。

Generation Size Adjustments

收集器保存的平均GC停顿时间等统计信息会在每次收集结束时更新。然后进行测试,以确定目标是否已达到,并对代的大小进行任何必要的调整。例外情况是,显式地GC(例如,对System.gc()的调用)会忽略保存统计信息和调整代的大小。

代的大小的增长和收缩是通过一个固定百分比(相对于代大小的百分比)来完成的,这样代就可以逐步的增加或减小,直到它的预期大小。增长和收缩的比率不同。默认情况下,代以20%的增量增长,以5%的增量收缩。年轻代增长的百分比由命令行选项'-XX:YoungGenerationSizeIncrement='控制,老年代由'-XX:TenuredGenerationSizeIncrement='控制。代(内存大小)收缩的百分比由命令行选项'-XX:AdaptiveSizeDecrementScaleFactor='调节。如果增长是百分之X,那么收缩的百分比是X/D。

如果收集器决定在启动时增加代(的内存大小),则在增量中添加一个补充百分比。这种补充随着收集次数的增加而减少,没有长期效果。该补充的目的是提高启动时性能。缩减的百分比没有补充。

如果没有达到最大GC停顿时间目标,则每次只收缩一个代的大小。如果两代的暂停时间都超过了目标,则首先收缩暂停时间较大的代的大小。

如果吞吐量目标没有得到满足,那么这两代的大小都会增加。每一个都按其对总垃圾收集时间的贡献的比例增加。
例如,如果年轻代的垃圾收集时间占总收集时间的25%(四分之一),如果年轻代的完全增量为20%,那么年轻代将增加5%(20%*25%)。

Default Heap Size

除非在命令行上指定了初始堆大小和最大堆大小,否则将根据计算机上的内存大小计算它们。

Client JVM Default Initial and Maximum Heap Sizes

默认的最大堆大小是物理内存大小(192 MB以下)的一半,否则是物理内存大小(1gb以下)的四分之一。

例如,如果您的计算机有128 MB的物理内存,那么最大堆大小为64 MB,如果大于或等于1 GB的物理内存,则最大堆大小为256 MB。

JVM实际上不会使用最大堆大小,除非您的程序创建了足够多的对象来需要它。在JVM初始化期间分配的数量要少得多,称为初始堆大小。这个数量至少是8mb,否则是物理内存的1/64(1GB以下)。

分配给年轻代的最大空间量是总堆大小的三分之一。

Server JVM Default Initial and Maximum Heap Sizes

默认的初始堆大小和最大堆大小在服务器JVM上的工作方式类似于在客户机JVM上的工作方式,只是默认值可以更高。在32位jvm上,如果有4 GB或更多的物理内存,默认的最大堆大小可以达到1 GB。在64位jvm上,如果有128 GB或更多的物理内存,默认的最大堆大小可以达到32 GB。您总是可以通过直接指定这些值来设置更高或更低的初始堆和最大堆。

Specifying Initial and Maximum Heap Sizes

可以使用命令行'-Xms'(初始堆值)和'-Xmx'(最大堆值)指定初始堆大小和最大堆大小。如果知道应用程序需要多少堆才能正常工作更好,可以将'-Xms'和'-Xmx'设置为相同的值。如果没有,JVM将从使用初始堆大小开始,然后增长Java堆,直到找到堆使用和性能之间的平衡。

其他参数和选项可以影响这些默认值。要验证默认值,请使用'-XX:+PrintFlagsFinal'选项并在输出中查找'MaxHeapSize'。例如,在Linux或Solaris上,可以运行以下命令:“java -XX:+PrintFlagsFinal -version | grep MaxHeapSize”。

Excessive GC Time and OutOfMemoryError

如果在垃圾收集中花费了太多的时间,并行收集器将抛出OutOfMemoryError:如果超过98%的总时间花费在垃圾收集中,并且回收的堆不足2%,那么将抛出OutOfMemoryError。该特性旨在防止应用程序长时间运行,同时由于堆太小而很少或没有进展。如果需要,可以通过在命令行中添加选项'-XX:-UseGCOverheadLimit'禁用此功能。

Measurements

并行收集器的详细垃圾收集器输出与串行收集器的输出本质上是相同的。

你可能感兴趣的:(java虚拟机,JDK8,垃圾回收调优指南,并行收集器)