并行收集器

并行收集器也称作吞吐量优先收集器,是一种跟串行收集器类似的分代/世代收集器。跟串行收集器不同的是,并行收集器使用多线程的方式来加速垃圾回收。

使用-XX:+UseParallelGC参数开启并行收集器。默认情况下,使用此参数后,minor收集和major收集都会采用并行的方式,进一步较少垃圾回收的负载。

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

在具有N(N大于8)个物理线程的机器上,串行收集器使用N的分数作为垃圾回收线程数量。

比例大约为5/8 * N。如果N小于8,则使用N作为线程数。在有些平台上,这个数值为5/16*N。此数值也可以通过命令行参数指定。在单处理器的机器上,并行收集器的表现可能不如Serial收集器,因为并行收集器有较高的开销,例如同步。当应用程序运行在配置了中大型堆内存的虚拟机上时,在双核环境下,并行收集略好与串行收集。在更多核的机器上,并行收集的性能优势更为显著。

可以通过-XX:ParallelGCThreads=<N>参数指定并行收集的线程数量。如果要通过调整堆内存大小来调优,那么并行收集器获得良好性能所需的堆大小跟串行收集所需的堆大小相同。启用并行收集应尽量缩短线程停顿,因为多线程收集参与minor回收,一些碎片化的对象可能在回收期间有年轻代晋升到老年代。
每一个垃圾回收线程参与到minor回收,且持有了一部分空间用于老年代晋升,将可用空间划分为“晋升缓冲”会导致碎片化。减少并行收集线程数量、增加老年代空间,可以降低碎片效应。

并行收集器中的世代分布

并行收集器的分代有所不同:

并行收集器的工程学

当使用-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/4,初始堆大小为物理内存的1/16。能分配给新生代的最大空间为堆总大小的1/3。

配置并行收集器的初始堆内存和最大堆内存

可以通过 -Xms 指定初始堆内存大小;通过-Xmx 指定最大堆内存大小

如果你知道应用程序需要使用多少内存,那么你可以将-Xms和-Xmx设置为相同的值。如果你不知道该用多少内存,那么JVM在启动时使用初始堆大小,在运行过程中增加堆内存,直到虚拟机在性能和用量之间找到平衡。

其它一些参数将影响这些默认值。可以通过-XX:+PrintFlagsFinal选项输出的-XX:MaxHeapSize信息来验证这些默认值。例如

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

过多的并行收集时间和OutOfMemoryError

如果垃圾收集耗费了过多的时间,并行收集器将抛出OOM错误。

如果超过98%的时间花费在垃圾回收上,且少于2%的堆内存被收回,将会抛出OutOfMemoryError。该功能的设计是为了防止应用程序在长时间运行下、堆内存逐渐减小的情况下长时间无响应。想要禁用该功能,可以通过-XX:-UseGCOverheadLimit命令。

并行收集器的测量

并行收集器的输出信息本质上跟串行收集器是一样的。