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));
}
}
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