簡介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


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