# 并行收集器

并行收集器和串行收集器类似。主要区别是使用了多线程来加速垃圾收集。用-XX:+UseParallelGC来开启。默认开启参数后,minor和major收集都会采用并行收集。

在N大于8的具有N个硬件线程的机器上,并行收集器使用N的固定分数作为垃圾收集器线程的数量。对于大的N值,分数约为5/8。在N值低于8时,使用的数字是N。在选定的平台上,分数下降到5/16。垃圾收集器线程的具体数量可以通过命令行选项(稍后描述)进行调整。在具有一个处理器的主机上,由于并行执行(例如同步)所需的开销,并行收集器的性能可能不会像串行收集器那样好。然而,当运行具有中型到大型堆的应用程序时,在具有两个处理器的机器上,它通常比串行收集器少一些,并且当有两个以上的处理器可用时,其性能通常明显优于串行收集器。

可以使用命令行选项-XX:ParallelGCThreads=<N>控制垃圾收集器线程的数量。如果使用命令行选项对堆进行显式调整,那么使用并行收集器实现良好性能所需的堆的大小与串行收集器所需的大小相同。然而,启用并行收集器应该会缩短收集暂停时间。由于多个垃圾收集器线程正在参与一个小收集,由于收集期间从年轻一代晋升为老年一代,一些碎片化是可能的。参与次要收集的每个垃圾收集线程都会保留老年代的一部分进行推广,并将可用空间划分为这些“促销缓冲区”可能会导致碎片化效应。减少垃圾收集器线程的数量并增加老年代的规模将减少这种碎片化效应。

# 并行收集器Ergonomics

默认server-class机器会选择并行收集器。并行收集器会自动调整,来使得满足特定的行为。

  • 最大垃圾收集停顿时间(Maximum Garbage Collection Pause Time)。-XX:MaxGCPauseMillis=<N>。默认没有最大停顿时间的目标。如果设置了这个选项,堆大小和其他垃圾收集器相关的参数会动态调整,来尽量确保垃圾收集停顿时间小于这个值。这些调整可能会导致垃圾收集器减少应用的吞吐量,并且这个目标不一定一直满足。
  • 吞吐量(Throughput)。指花在垃圾收集上的时间/其他应用时间。-XX:GCTimeRatio=<N>,计算方式为让上述比值在1/(1 + <N>)以上。
  • 资源占用大小(Footprint)。最大堆设置-Xmx<N>,垃圾收集器会尽可能缩小堆大小来保证上述目标被满足。

目标的优先级为:

  1. 最大垃圾收集停顿时间。
  2. 吞吐量。
  3. 最小资源占用。

# 分代调整

每次垃圾收集之后(主动垃圾收集除外,例如System#gc()),都会更新垃圾收集器中的统计信息。

分代的大小每次膨胀或缩小是按照固定比例来完成的,以便于每个分代按照需要的大小来调整。膨胀和缩小比例是不一样的。默认膨胀每次20%,缩小每次5%。

膨胀比例参数:年轻代-XX:YoungGenerationSizeIncrement=<Y>,老年代-XX:TenuredGenerationSizeIncrement=<T>

缩小比例参数:-XX:AdaptiveSizeDecrementScaleFactor=<D>,如果膨胀调整系数为X %,那么每次缩小比例为X/D %。

  • 为了提高启动时的性能,在启动时分代增长会增加一个附加比例。缩小时不会有附加比例。
  • 如果最大停顿时间不满足,每次只会缩小其中的一个分代。如果两个分代的最大停顿时间都不满足,其中最大的停顿时间的分代会被先缩小。
  • 如果吞吐量不满足,两个分代都会膨胀。每个都根据其对总垃圾收集时间的贡献而增加。例如,如果年轻一代的垃圾收集时间是总收集时间的25%,如果年轻一代的全面增量为20%,那么年轻一代将增加5%。
  • 如果没有指定初始和最大堆大小,堆大小按照机器内存来计算。最大堆大小通过-XX:+PrintFlagsFinal来输出。
  • 并行收集器会抛出OutOfMemoryError如果GC时间太长。如果超过98%的总时间都用在gc上面并且少于2%的堆被释放,那么就会抛错。这样设计是避免应用因为堆太小而影响运行。如果必要的话,可以设置-XX:-UseGCOverheadLimit来关闭这个功能。