java HotSpot虚拟机垃圾回收优化(六、The Parallel Collector)

时间:2022-12-27 15:14:33

Parallel Collector是一个串行垃圾收集器相似的分代垃圾收集器,也被称为 throughput collector,即吞吐量垃圾收集器。
两者最大的区别就在于并行垃圾收集器多线程加速垃圾收集。并行垃圾收集器通过-XX:+UseParallelGC命令行参数启用。默认
情况下如果启用,minor和major collections垃圾回收都会并行执行,以进一步减少垃圾回收带来的开销。

在一个具有N个硬件线程的机器上,当N大于8时,并行垃圾收集器使用N的因定比例的线程数目用于垃圾回收,这个比例大约为
5/8(在被选定的平台上,这个比例降为5/16),当N小于8时,并行垃圾收集器则使用固定N个线程用于垃圾回收。并行垃圾收
集器使用的线程数是可以通过命令行选项调整的(稍后再讲)。在单处理器的机器上,由于并行执行的开销,并行垃圾收集器
很可能还没有串行垃圾器性能好,但是,在多处理器、中等或者较大堆空间的机器上,并行垃圾收集器则远比串行垃圾收集器
执行的好。

并行垃圾收集器使用线程的个数可以用命令行参数-XX:ParallelGCThreads=。如果通过命令行显式的调整堆空间,那么为了
良好的性能,并行垃圾收集器需要的堆空间大小同串行垃圾收集器一样,但是理论上启用并行垃圾收集器可使垃圾回收暂停时
间更短。因多个垃圾回收线程参与到一个minor collection过程中,将对象从年轻代提升到老年代时可能会产生内存碎片:每
个参与minor collection的垃圾回收线程保留老年代的一部分空间用于提升年轻代中对象,这部分空间也称为升级缓冲区,而
将可用空间划分为升级缓冲区操作可能产生内存碎片。减少垃圾回收线程数目与增大老年代空间可减少内存碎片的影响。

并行垃圾收集器默认在服务器类别的机器上被选择。另外,并行垃圾收集器支持通过指定特殊行为而不是指定代空间的大小来
自动调整和一些更细粒度的调整。我们可以指定的有最大垃圾回收暂停时间、吞吐量、堆空间占用。

Maximum Garbage Collection Pause Time:最大暂停时间目标可以通过命令行选项-XX:MaxGCPauseMillis=指定,此选项表
示暗示并行垃圾收集器N毫秒或者更少的垃圾回收暂停时间是期望的,默认情况下是没有最大垃圾回收暂停目标的。如果此目标
被指定,堆空间大小和其他垃圾回收相关的参数会自动被调整以图保持垃圾回收的暂停时间少于指定的值,这种调整可能会减
少应用的吞吐量,并且期望的暂停时间可能并不总是被满足。

Throughput:吞吐量目标是根据花费在垃圾回收的时间与应用程序除垃圾回收外运行的时间(也称应用时间)的对比来衡量的。
此目标可通过命令行参数 -XX:GCTimeRatio=来指定,表示垃圾回时间占总时间的比例是1/(1+N)。此目标默认为99,也就是
说垃圾回收时间为总时间的1%。

Footprint:最大堆空间占用是通过命令行参数-Xmx来指定的,另处,此垃圾收集器有一个隐含的目标,就是只要满足其他
目标,尽可能的减小堆空间大小。

注:这三个目标的优先级自上而下依次降低。

此垃圾回收器持有的一些统计,比如平均暂停时间,在每次垃圾回收的最后都会被更新,确定垃圾回收是否满足目标的测试和各
代空间大小的必要调整也是那时候做的。就保持统计数据和调整代空间大小而言,明显的垃圾回收(例如调用System.gc())是
忽略这些操作的。

增加或者缩减代空间大小是通过一个代空间大小的固定百分比的增量来做的,以便逐步增加或者缩减到期望的大小。增加和缩减
堆空间大小的比率是不一样的,默认情况下,代空间每次增加的比率为百分之二十,减少的比率是百分之五,增加的百分比年轻
代可以通过命令行参数-XX:YoungGenerationSizeIncrement=指定,老年代可以通过 -XX:TenuredGenerationSizeIncrement
=来指定;减少代空间则是通过命令行参数-XX:AdaptiveSizeDecrementScaleFactor=来指定。如果代空间增加的增量是百
分之X,那么代空间减少的减量是百分之X除以D。

如果此垃圾收集器在启动时决定增加代空间的大小,那么就会有一个对应的补充的百分比增量。这个增量随着垃圾回收的次数
衰败并且没有长期的影响。设计此补充百分比增量是为了提高系统启动时性能。代空间缩减时则没有此增量。

如果最大暂停时间目标没有被满足,那么在同一时间只会有一个代空间的大小会减少。如果所有代空间的垃圾回收时间都超过
设置的目标值,那么最长垃圾回收时间的那个代空间大小会首先减少。

如果吞吐量目标没有被满足,那么所有代空间大小都会增加。每个代空间增加的大小与它们各自对总垃圾回收时间的贡献成比
例。例如:如果年轻代垃圾回收的时间占总垃圾回收时间的25%,如果年轻代的一次性增量为20%,那么年轻代还会再被增加5%。

并行垃圾收集器的堆大小如果没有在初始化时在命令行指定,那么将依据机器内存自动计算。

客户端JVM默认最大堆内存和初始化堆内存:
如果物理内存小于1G,一般为物理内存的二分之一,上限为192MB;
如果物理内存大小1G,为1G的四分之一。

最大堆内存空间大小并不是JVM实际使用的大小,除非应用创建足够的对象请求内存。JVM初始化时一般只分配一块很小的堆空间,
至少8M或者内存小于1G时的64分之一。

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

服务器端VM默认最大堆内存和初始化堆内存:
除了以下几点,其他同客户端相似:
(1)在32位JVM上,如果内存大于等于4G时,最大堆空间大小可达到1G;
(2)在64位JVM上,如果物理内存大于等于128G时,最大堆内存空间大到可达32G。

指定初始化堆内存与最大堆内存:
可以通过 -Xms指定初始化堆空间大小,-Xmx指定最大堆空间大小。如果清楚的知道自己的应用使用多大的内存,那么可以将
此两项都设置为那个值,如果不清楚,那么JVM将以初始化的堆空间大小启动,然后遂步增加堆内存直到达到一个堆空间使用
率与性能的平衡点。

如果要验证设置的默认值,可以通过选项-XX:+PrintFlagsFinal选项,并且在输出中查找MaxHeapSize 。如在Linux或者solaris
上可以:
java -XX:+PrintFlagsFinal -version | grep MaxHeapSize

如果有太多的时间花费在垃圾回收上,并行垃圾收集器会抛出OutOfMemoryError:具体为如果总时间的98%花费在垃圾回收上并
且少于堆空间的2%被恢复时,抛出OutOfMemoryError。此种机制的目的在于防止因堆空间太小而应用程序运行了很长一段时间
却取得很少或者没有的情况。如果有必要,可以通过添加命令行参数 -XX:-UseGCOverheadLimit 来禁用此机制。

并行垃圾收集器的详细输出同串行垃圾收集器输出本质上一样。