简介JVM的Parallel Scavenge及Parallel Old垃圾收集器

Parallel Scavenge:

是与ParNew类似,都是用于年轻代回收的使用复制算法的并行收集器,与ParNew不同的是,Parallel Scavenge的目标是达到一个可控的吞吐量,吞吐量=程序运行时间/(程序运行时间+GC时间),如程序运行了99s,GC耗时1s,吞吐量=99/(99+1)=99%。Parallel Scavenge提供了两个参数用以精确控制吞吐量,分别是用以控制最大GC停顿时间的-XX:MaxGCPauseMillis及直接控制吞吐量的参数-XX:GCTimeRatio.

MaxGCPauseMiilis:单位为ms,适用于高用户体验的场景,虚拟机将尽力保证每次MinorGC耗时不超过所设时长,但并不是该时间越小越好,因为GC耗时缩短是用调小年轻代获取的,回收500m的对象肯定要比回收2000m的对象耗时更短,但是回收频率也大大增大了,吞吐量也随之下去了。使用该参数的理论效果:MaxGCPauseMillis越小,单次MinorGC的时间越短,MinorGC次数增多,吞吐量降低。

GCTimeRatio:从字面意思上理解是花费在GC上的时间占比,但是实际含义并非如此,GC耗时的计算公式为1/(1+n),n为GCTimeRatio,因此,GCTimeRatio的实际用途是直接指定吞吐量。GCTimeRatio的默认值为99,因此,GC耗时的占比应为1/(1+99)=1%。使用参数的理论效果:GCTimeRatio越大,吞吐量越大,GC的总耗时越小。有可能导致单次MinorGC耗时变长。适用于高运算场景。

此外,还有个参数和以上两个参数息息相关,那就是-XX:+UseAdaptiveSizePolicy,默认为启用,搭配MaxGCPauseMillis或GCTimeRatio使用,打开该开关后,虚拟机将根据当前系统运行情况收集性能监控信息,动态调整SurvivorRatio,PretenureSizeThreshold等细节参数。

使用方式:该收集器是server模式下的默认收集器,也可-XX:+UseParallelGC强制使用该收集器,打开该收集器后,将使用Parallel Scavenge(年轻代)+Serial Old(老年代)的组合进行GC。


Parallel Old:

是Parallel Scavenge收集器的老年代版本,用于老年代的垃圾回收,但与Parallel Scavenge不同的是,它使用的是“标记-整理算法”。适用于注重于吞吐量及CPU资源敏感的场合。

使用方式:-XX:+UseParallelOldGC,打开该收集器后,将使用Parallel Scavenge(年轻代)+Parallel Old(老年代)的组合进行GC。


简单测试下Parallel Scavenge收集器及其参数MaxGCPauseMillis、GCTimeRatio、UseAdaptiveSizePolicy的使用效果,PS是年轻代收集器,只测试MinorGC。

另:参数调整、测试结果及结论我都写在了注释里,方便对比结果。

import java.util.Random;

/**
 * 测试ParallelGC
 * -Xms3072m -Xmx3072m -Xmn1536m -XX:+PrintGCDetails -XX:+UseParallelGC -verbose:gc
 * 
 * =======================Test MaxGCPauseMillis=1====================
 * -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=1 
 * 第一次:
 * runtime=312055.243429ms
 * GCtime:2150次,耗时1965ms
 * GC耗时占比0.6297%,吞吐量为99.37%,GC平均耗时0.9140ms
 * 
 * 第二次:
 * runtime=292728.022371ms
 * GCtime:2137次,耗时1821ms
 * GC耗时占比0.6221%,吞吐量为99.38%,GC平均耗时0.8521ms
 * 
 * 第三次:
 * runtime=290375.406815ms
 * GCtime:2147次,耗时1898ms
 * GC耗时占比0.6536%,吞吐量为99.35%,GC平均耗时0.8840ms
 * 测试结论:尽管设置了-Xmn大小,但查看GC日志可发现,新生代内存大小在变化,单个minorGC耗时控制在1ms以内,但是个别会超过1ms,基本满足该参数效果
 * =======================Test MaxGCPauseMillis=50====================
 * -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=50
 * 第一次:
 * runtime=300654.553462ms
 * GCtime:2155次,耗时1789ms
 * GC耗时占比0.5950%,吞吐量为99.40%,GC平均耗时0.8302ms
 * 
 * 第二次:
 * runtime=301405.588849ms
 * GCtime:2164次,耗时1759ms
 * GC耗时占比0.5836%,吞吐量为99.42%,GC平均耗时0.8128ms
 * 
 * 第三次:
 * runtime=302787.210161ms
 * GCtime:2147次,耗时1884ms
 * GC耗时占比0.6222%,吞吐量为99.38%,GC平均耗时0.8775ms
 * 
 * 测试结论:理论上随着MaxGCPauseMillis调大,吞吐量会逐渐变大,以上测试结果该指标也显示出了微弱的提升
 * 
 * =======================Test GCTimeRatio=99 -XX:-UseAdaptiveSizePolicy VS -XX:+UseAdaptiveSizePolicy====================
 * =======================-XX:-UseAdaptiveSizePolicy=======================
 * 第一次:
 * runtime:294789.096291ms  
 * GCtime:2894次,耗时2366ms,GC耗时占比0.8026%,minorGC平均耗时0.8176ms
 * 吞吐量:99.02%
 * 
 * 第二次:
 * runtime:290129.766897ms  
 * GCtime:2879次,耗时2783ms,GC耗时占比0.9592%,minorGC平均耗时0.9667ms
 * 吞吐量:99.04%
 * 
 * 第三次:
 * runtime:293696.243212ms  
 * GCtime:2889次,耗时2220ms,GC耗时占比0.7559%,minorGC平均耗时0.7684ms
 * 吞吐量:99.24%
 *
 * =======================-XX:+UseAdaptiveSizePolicy=======================
 * 第一次:
 * runtime:298242.478321ms  
 * GCtime:2161次,耗时1746ms,GC耗时占比0.5854%,minorGC平均耗时0.8080ms
 * 吞吐量:99.42%
 * 
 * 第二次:
 * runtime:290987.165009ms  
 * GCtime:2139次,耗时1427ms,GC耗时占比0.4904%,minorGC平均耗时0.6671ms
 * 吞吐量:99.51%
 * 
 * 第三次:
 * runtime:294726.818532ms  
 * GCtime:2140次,耗时1554ms,GC耗时占比0.5273%,minorGC平均耗时0.7262ms
 * 吞吐量:99.47%
 *
 * 测试结论:相比于关闭UseAdaptiveSizePolicy参数,打开该参数后,GC次数及GC耗时的占比还是有明显优化的,吞吐量也有较为明显的提高
 * =======================Test GCTimeRatio=99 VS GCTimeRatio=79 VS GCTimeRatio=49=======================
 * =======================-XX:GCTimeRatio=79=======================
 * 理论GC耗时占比不超过1.25%
 * 第一次:
 * runtime:290639.389875ms
 * GCtime:2141次,耗时1476ms,GC耗时占比0.5078%,minorGC平均耗时0.6894ms
 * 吞吐量:99.49%
 * 
 * 第二次:
 * runtime:295632.420638ms
 * GCtime:2141次,耗时1596ms,GC耗时占比0.5399%,minorGC平均耗时0.7454ms
 * 吞吐量:99.46%
 * 
 * 第三次:
 * runtime:293121.19935ms
 * GCtime:2136次,耗时1737ms,GC耗时占比0.5926%,minorGC平均耗时0.8132ms
 * 吞吐量:99.40%
 * 
 * =======================-XX:GCTimeRatio=49======================= 
 * 理论GC耗时不超过2%
 * 第一次:
 * runtime:296832.913722ms
 * GCtime:2141次,耗时1570ms,GC耗时占比0.5289%,minorGC平均耗时0.7333ms
 * 吞吐量:99.28%
 * 
 * 第二次:
 * runtime:299285.966386ms
 * GCtime:2157次,耗时1601ms,GC耗时占比0.5349%,minorGC平均耗时0.7422ms
 * 吞吐量:99.46%
 * 
 * 第三次:
 * runtime:290825.40114ms
 * GCtime:2142次,耗时1561ms,GC耗时占比0.5367%,minorGC平均耗时0.7288ms
 * 吞吐量:99.46%
 * 
 * 测试结论:理论上随着GCTimeRatio数值的调大, 吞吐量应该是越大,并且GC耗时占比应该是越小的,可能是测试代码的原因,
 * 这两个指标随着GCTimeRatio参数的变大没有明显的差距,并且实际吞吐量远大于所设置的理论吞吐量,实际耗时占比也远优于所设置的理论耗时占比
 * 
 * ====================compare MaxGCPauseMillis && GCTimeRatio====================
 * -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMills=1 -XX:GCTimeRatio=99
 * 第一次:
 * runtime=308365.010339ms
 * GCtime:2150次,耗时2519ms,GC耗时占比0.8169%,minorGC平均耗时1.1716ms
 * 吞吐量:99.18%
 * 
 * 第二次:
 * runtime=298268.442261ms
 * GCtime:2156次,耗时2188ms,GC耗时占比0.7336%,minorGC平均耗时1.0148ms
 * 吞吐量:99.27%
 * 
 * 第三次:
 * runtime=313188.40367ms
 * GCtime:2146次,耗时1995ms,GC耗时占比0.6370%,minorGC平均耗时0.9296ms
 * 吞吐量:99.36%
 * 
 * -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMills=50 -XX:GCTimeRatio=49
 * 第一次:
 * runtime=304944.569025ms
 * GCtime:2149次,耗时1682ms,GC耗时占比0.5516%,minorGC平均耗时0.7827ms
 * 吞吐量:99.45%
 * 
 * 第二次:
 * runtime=307158.617253ms
 * GCtime:2135次,耗时2256ms,GC耗时占比0.7345%,minorGC平均耗时1.0567ms
 * 吞吐量:99.27%
 * 
 * 测试结论:MaxGCPauseMiilis 与  GCTImeRatio 参数不要同时配置
 * 
 * @author ljl
 */
public class TestParallelGCOfNewSize {
	private static int _10MB = 10 * 1024 * 1024;

	public static void main(String[] args) throws InterruptedException {
	  Random rm = new Random();
	  int j ;
	  long startTime = -System.nanoTime();
      for(int i=0;i<30000;i++){
    	  j = rm.nextInt(20) + 1;
    	  byte[] memory = new byte[j *_10MB];
      }
      System.out.println("runTime = "+(System.nanoTime()+startTime));
	}
}

简单测试下-XX:+UseParallelGC 对比 -XX:+UseParallelOldGC的使用效果

import java.util.Random;

/**
 * 测试ParallelGC VS ParallelOldGC
 * 
 * ====================Test -XX:+UseParallelGC + -XX:-UseAdaptiveSizePolicy====================
 * -Xms3072m -Xmx3072m -Xmn1536m -XX:+PrintGCDetails -XX:-UseAdaptiveSizePolicy
 * 第一次:
 * runtime=651690.230011ms,
 * 新生代GCtime:2499次,耗时261322ms,平均GC耗时104.5706ms
 * 老年代GCtime:1249次,耗时157339ms,平均GC耗时125.9720ms
 * 总计:GCtime:3748次,耗时418661ms,吞吐量35.76%
 * 
 * 第二次:
 * runtime=641059.764857ms,
 * 新生代GCtime:2499次,耗时255108ms,平均GC耗时102.0840ms
 * 老年代GCtime:1249次,耗时155706ms,平均GC耗时124.6645ms
 * 总计:GCtime:3748次,耗时410814ms,吞吐量35.92%
 * 
 * 第三次:
 * runtime=666173.036551ms,
 * 新生代GCtime:2499次,耗时266824ms,平均GC耗时106.7723ms
 * 老年代GCtime:1249次,耗时158376ms,平均GC耗时126.8022ms
 * 总计:GCtime:3748次,耗时425200ms,吞吐量36.17%
 * 
 * ====================Test -XX:+UseParallelGC + -XX:+UseAdaptiveSizePolicy====================
 * -Xms3072m -Xmx3072m -Xmn1536m -XX:+PrintGCDetails -XX:+UseAdaptiveSizePolicy
 * 第一次:
 * runtime=508986.377663ms,
 * 新生代GCtime:1668次,耗时172866ms,平均GC耗时103.6367ms
 * 老年代GCtime:833次,耗时103615ms,平均GC耗时124.3876ms
 * 总计:GCtime:2501次,耗时276481ms,吞吐量45.68%
 * 
 * 第二次:
 * runtime=515247.965156ms,
 * 新生代GCtime:1668次,耗时175774ms,平均GC耗时105.3801ms
 * 老年代GCtime:833次,耗时104826ms,平均GC耗时125.8415ms
 * 总计:GCtime:2501次,耗时280600ms,吞吐量45.54%
 * 
 * 第三次:
 * runtime=509899.992038ms,
 * 新生代GCtime:1668次,耗时173997ms,平均GC耗时104.3147ms
 * 老年代GCtime:833次,耗时102929ms,平均GC耗时123.5642ms
 * 总计:GCtime:2501次,耗时276926ms,吞吐量45.69%
 * 
 * 测试结论:开启UseAdaptiveSizePolicy参数后,MinorGC次数明显下降,受此影响,老年代的GC次数也明显下降,总GC耗时明显下降,吞吐量也得到了显著提升,
 * 新生代及老年代的GC平均耗时变化不大
 * 
 * ====================Test -XX:+UseParallelOldGC + -XX:+UseAdaptiveSizePolicy====================
 * -Xms3072m -Xmx3072m -Xmn1536m -XX:+PrintGCDetails -XX:+UseAdaptiveSizePolicy -XX:+UseParallelOldGC
 * 第一次:
 * runtime=518429.776807ms,
 * 新生代GCtime:865次,耗时93305ms,平均GC耗时107.8671ms
 * 老年代GCtime:432次,耗时53509ms,平均GC耗时123.8634ms
 * 总计:GCtime:1297次,耗时146814ms,吞吐量71.68%
 * 
 * 第二次:
 * runtime=506578.288859ms,
 * 新生代GCtime:1668次,耗时172922ms,平均GC耗时103.6703ms
 * 老年代GCtime:833次,耗时103334ms,平均GC耗时124.0504ms
 * 总计:GCtime:2501次,耗时276256ms,吞吐量45.47%
 * 
 * 第三次:
 * runtime=518472.380237ms,
 * 新生代GCtime:1681次,耗时177309ms,平均GC耗时105.4783ms
 * 老年代GCtime:840次,耗时104481ms,平均GC耗时124.3821ms
 * 总计:GCtime:2521次,耗时281790ms,吞吐量45.65%
 * 
 * 测试结论:相比Serial Old收集器,Parallel Old收集器对于老年代的GC并没有明显优势,可能受制于测试电脑的cpu个数(4核)
 * 
 * @author ljl
 */
public class TestParallelGC {
	private static int _10MB = 10 * 1024 * 1024;

	public static void main(String[] args) {
		byte[] memory = null;
		Random rm = new Random();
		int j;
		long startTime = -System.nanoTime();
		for (int i = 0; i < 5000; i++) {
			j = rm.nextInt(30) + 10;
			memory = new byte[50 * _10MB];
		}
		System.out.println("runtime = " + (System.nanoTime() + startTime));
	}
}


另外,笔者在测试UseParallelGC的时候,遇到一个比较困惑的问题:以上代码,第一次创建800m的对象时,分配到了Eden区,第二次Eden区空间不足,理应触发minorGC,将Eden区的对象复制到old,但并没有,而是直接将新的对象分配到了old,并且,使用ParNewGC时,是正常触发minorGC的, 另外,分700||800||900m都是这样,分500m会正常触发minorGC,欢迎大神解疑或讨论,附问题贴链接:http://bbs.csdn.net/topics/392170364


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章