The Parallel Collector

并行收集器(此处也称为吞吐量收集器)是类似于串行收集器的分代收集器。

串行和并行收集器之间的主要区别在于并行收集器具有多个线程,用于加速垃圾收集。

使用命令行选项-XX:+UseParallelGC启用并行收集器。

默认情况下,使用此选项,可以并行运行次要集合和主要集合,以进一步减少垃圾收集开销。

并行收集器垃圾收集器线程数

在具有<N>硬件线程且<N>大于8的机器上,并行收集器使用固定分数<N>作为垃圾收集器线程的数量。

对于<N>的大值,该分数约为5/8。在<N>低于8的值时,使用的数字是<N>

在选定的平台上,分数降至5/16。可以使用命令行选项(稍后描述)调整垃圾收集器线程的特定数量。在具有一个处理器的主机上,由于并行执行所需的开销(例如,同步),并行收集器的性能可能不如串行收集器。但是,在运行具有中型到大型堆的应用程序时,它通常在具有两个处理器的计算机上以适度的数量优于串行收集器,并且当有两个以上的处理器可用时,通常比串行收集器的性能要好得多。

可以使用命令行选项-XX:ParallelGCThreads=<N>来控制垃圾收集器线程的数量。

如果使用命令行选项调整堆,则使用并行收集器获得良好性能所需的堆大小与串行收集器所需的大小相同。

但是,启用并行收集器应使收集暂停时间更短。因为多个垃圾收集器线程正在参与次要集合,所以由于在收集期间从年轻一代到老一代的促销,一些碎片是可能的。

次要集合中涉及的每个垃圾收集线程保留旧一代的一部分用于促销,并且将可用空间划分为这些“促销缓冲区”可能导致碎片效应。

减少垃圾收集器线程的数量并增加旧代的大小将减少这种碎片效应。

并行收集器中的世代安排

并行收集器中代的排列是不同的。

这种安排如图6-1所示:

  • 图6-1并行收集器中的生成排列

jsgct_dt_002_armgnt_gn_pl_new.png

并行收集器人体工程学

当使用-XX:+UseParallelGC选择并行收集器时,它启用自动调整方法,允许您指定行为而不是生成大小和其他低级调整细节。

指定并行收集器行为的选项

您可以指定最大垃圾收集暂停时间,吞吐量和占用空间(堆大小)。

最大垃圾收集暂停时间:使用命令行选项-XX:MaxGCPauseMillis=<N>指定最大暂停时间目标。

这被解释为需要<N>毫秒或更短的暂停时间的提示;默认情况下,没有最大暂停时间目标。

如果指定了暂停时间目标,则调整堆大小和与垃圾收集相关的其他参数,以试图使垃圾收集暂停时间短于指定值;但是,可能并不总是满足所需的暂停时间目标。这些调整可能导致垃圾收集器降低应用程序的总吞吐量。

吞吐量:吞吐量目标是根据垃圾收集所花费的时间与垃圾收集之外所花费的时间(称为应用程序时间)来衡量的。

目标由命令行选项-XX:GCTimeRatio=<N>指定,它将垃圾收集时间与应用程序时间的比率设置为 1/(1+<N>)

例如,-XX:GCTimeRatio=19设置目标为垃圾收集的总时间的1/20或5%。默认值为99,导致垃圾回收的目标为1%。

占用空间:使用选项-Xmx <N>指定最大堆占用空间。此外,只要满足其他目标,收集器就有一个隐含的目标,即最小化堆的大小。

并行收集器目标的优先级

目标是最大暂停时间目标,吞吐量目标和最小足迹目标,并按以下顺序处理目标:

首先满足最大暂停时间目标。只有在满足后才能解决吞吐量目标。同样,只有在满足前两个目标之后才考虑足迹目标。

并行收集器生成大小调整

收集器保存的平均暂停时间等统计信息在每个集合结束时更新。

然后进行确定是否满足目标的测试,并对一代的尺寸进行任何所需的调整。

例外的是,显式垃圾收集(例如,对 System.gc() 的调用在保持统计数据和调整代数大小方面被忽略。

增长和缩小一代的尺寸是通过增量来完成的,这些增量是一代的尺寸的固定百分比,以便一代产生向上或向下朝向其期望的尺寸。成长和萎缩以不同的速度完成。默认情况下,一代以20%的增量增长,并以5%的增量收缩。

增长百分比由年轻代的命令行选项 -XX:YoungGenerationSizeIncrement = <Y> 和旧代的-XX:TenuredGenerationSizeIncrement = <T>控制。

通过命令行标志-XX:AdaptiveSizeDecrementScaleFactor = <D>调整生成收缩的百分比。

如果增长增量为X%,则收缩的减少量为X / D%。

如果收集器决定在启动时增长一代,则会在增量中添加补充百分比。这种补充剂随着收集的数量而衰减,并且没有长期影响。补充的目的是提高启动性能。萎缩的百分比没有补充。

如果未满足最大暂停时间目标,则一次只缩小一代的大小。如果两代的暂停时间都高于目标,那么具有较大暂停时间的代的大小首先缩小。

如果未满足吞吐量目标,则增加两代的大小。每个都与其对垃圾收集总时间的贡献成比例地增加。例如,如果年轻一代的垃圾收集时间是总收集时间的25%,并且如果年轻一代的完全增量将是20%,那么年轻一代将增加5%。

并行收集器默认堆大小

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

默认的最大堆大小是物理内存的四分之一,而初始堆大小是物理内存的1/64。分配给年轻代的最大空间量是总堆大小的三分之一。

并行收集器初始和最大堆大小的规范

您可以使用-Xms(初始堆大小)和-Xmx(最大堆大小)选项指定初始和最大堆大小。

如果您知道应用程序需要多少堆才能正常工作,那么您可以将-Xms和-Xmx设置为相同的值。如果您不知道,那么JVM将首先使用初始堆大小,然后增加Java堆,直到它在堆使用和性能之间找到平衡。

其他参数和选项可能会影响这些默认值。

要验证默认值,请使用-XX:+PrintFlagsFinal选项并在输出中查找-XX:MaxHeapSize。

例如,在Linux或Solaris上,您可以运行以下命令:

java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize

过多的并行收集器时间和OutOfMemoryError

如果在垃圾收集(GC)中花费了太多时间,则并行收集器会抛出OutOfMemoryError。

如果超过98%的总时间花在垃圾收集上并且不到2%的堆被恢复,则抛出OutOfMemoryError。

此功能旨在防止应用程序长时间运行,同时由于堆太小而很少或没有进度。

如有必要,可以通过在命令行中添加选项 -XX:-UseGCOverheadLimit 来禁用此功能。

并行收集器测量

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

参考资料

https://docs.oracle.com/javase/9/gctuning/parallel-collector1.htm