并行收集器(也称为吞吐量收集器)是一种类似于串行收集器的分代收集器。串行收集器和并行收集器的主要区别是并行收集器有多个线程,这些线程用于加速垃圾收集。
通过命令行选项-XX:+UseParallelGC启用并行收集器。默认情况下,使用这个选项,次要(Minor)和主要(Major)都并行收集,以进一步减少垃圾收集开销。
并行收集器的垃圾收集器线程数
当机器上硬件线程数量为N(N>8)时,并行收集器使用N乘以固定比例作为垃圾收集器线程的数量。
当N较大时,这个比例一般接近5/8,当N小于8时,线程数设置为N。部分平台上,比例被设置为5/16。垃圾收集器的线程数量可以通过命令选项手动指定。在单处理器的主机上,由于并行需要一定的开销(例如线程同步),并行收集器的性能可能不如串行收集器。但是,当运行具有中等到大型堆的应用程序时,在双处理器的机器上,它的性能通常要优于串行收集器,在两个以上处理器可用时,它的性能会比串行收集器好很多。
可以通过-XX:ParallelGCThreads=
<N>来指定并行收集使用的线程数量,如果需要对堆大小进行调优,并行收集器的堆大小可以与串行收集器取得良好性能时的堆大小一致。但是,并行收集器理论上会缩短收集暂停时间。因为多个垃圾收集器线程参与了一个小的收集(Minor Collection),所以在收集器间从年轻代到老年代可能会产生一些内存碎片。小的收集(Minor Collection)中涉及到的每个垃圾收集线程分别在老年代中保留一部分内存,而将可用内存分别划分可能导致碎片效应。减少垃圾收集器线程数量和增加老年代的大小可以减少碎片。
并行收集器中代的划分
在并行收集器中,分代的排列与串行收集器有所区别。
下图是并行收集器中分代示例:
并行收集器调优
使用-XX:+UseParallelGC启用并行收集器,并行收集器提供了一种自动优化方法,可以让我们通过指定性能目标而不是指定代的大小和其他低级的调优细节来优化性能。
调整并行收集器的行为
并行收集器可以指定最大垃圾收集暂停时间,吞吐量以及内存占用:
1.最大暂停时间:XX:MaxGCPauseMillis=
<N>可以指定程序最大暂停时间,这个命令会通知垃圾收集器,暂停时间应该在<N>或者<N>以下,默认不指定最大暂停时间。当指定了最大暂停时间,垃圾收集器会尝试调整堆大小和其他垃圾收集相关的参数来使程序的暂停时间短于设定的值,但一般设定的期望值可能会存在达不到的情况,这种情况下,垃圾收集器的自动调整可能导致应用程序的总吞吐量降低。
2.吞吐量:吞吐量目标是根据进行垃圾收集所花费的时间与进行垃圾收集以外的时间(称为应用程序时间)来度量的。由命令行选项-XX:GCTimeRatio=<N>指定,该选项将垃圾收集时间与应用程序时间的比率设置为1 / (1 + <N>),例如N=19,表示垃圾收集时间占总程序运行时间的1/(1+19),即5%。
3.内存占用:-Xmx<N>可以指定最大内存,通常情况下,收集器会在满足其他目标的情况下最小化堆的大小。
并行收集器性能目标的优先级
当性能目标有最大暂停时间、吞吐量和内存占用时,目标的优先级为:
1.最大暂停时间
2.吞吐量
4.内存占用
调整并行收集器分代大小
垃圾收集器会在每次收集完成时统计垃圾收集相关信息,然后会测试性能是否达到指定的目标,未达到目标时会调整各个分代的规模,显式指定垃圾回收除外(例如,手动调用System.gc()收集过程中的信息不会被用于性能优化)。
代的大小增加和缩小都是通过递增或递减方式完成的,每次变化量都是代大小的固定百分比,这样代大小会逐步增加或减少,并且增大和缩小的速度是不同的。默认情况下,增大时百分比为20%,缩小时百分比为5%。使用-XX:YoungGenerationSizeIncrement=
<Y>可以设置年轻代每次增大百分比,使用-XX:TenuredGenerationSizeIncrement=
<T> 设置老年代每次增大百分比;代缩小的百分比由-XX:AdaptiveSizeDecrementScaleFactor=
<D>指定,如果增大的百分比为X%,则缩小的递减量为X/D%。
如果收集器决定启动时增大代大小,则增大百分比会额外加上一个百分比,这个额外百分比随着垃圾收集次数增加而减少且不会造成长期影响。这个额外百分比目的主要是为了提高程序的启动性能,而缩小时则不会加上额外的百分比。
如果指定的目标未达成,则收集器每次会缩小其中一个代的大小,如果两个代的暂停时间都高于目标,那么暂停时间较大的代大小会先缩小。
如果吞吐量目标未达成,则两个代的大小都会增加。增加的大小由它们各自占垃圾收集时间的比例决定。例如,如果年轻代垃圾收集时间占总垃圾收集时间的25%,若年轻代全部增量百分比为20%,那么年轻代会增大5%。
并行收集器默认堆大小
默认根据计算机上的内存计算最小堆和最大堆大小,除非通过命令行进行了指定,默认最小堆为物理内存的1/64,最大堆为物理内存的1/4,分配给年轻代的最大空间为堆大小的1/3。
指定并行收集器的初始和最大堆大小
可以使用选项-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来禁用此功能。
并行收集器指标
并行收集器的详细垃圾收集信息输出基本上与串行收集器的输出相同。