JVM之年輕代動態調整策略

多核心服務器配置下,jdk1.8默認使用 ParallelGC垃圾回收器,默認會開啓年輕代的內存動態調整策略(UseAdaptiveSizePolicy);可以使用jinfo命令查看。

#查看GC垃圾回收器
jinfo -flags PID

#查看動態調整策略是否開啓
jinfo -flag UseAdaptiveSizePolicy PID

動態調整策略主要影響Eden和Survivor內存分配(hotspot默認8:1:1,OpenJdk默認6:1:1),而這兩塊內存區域的分配,會直接影響到youngGC的頻率和執行時間;在某些情況下,例如大對象無法申請新生代空間,該對象會直接會進入老年代,從而間接影響full GC的頻率。

在jvm中,還有一個參數:-XX:MaxGCPauseMillis(默認200ms) 可以設置年輕代gc的執行時間;這是一個軟指標,jvm會盡量優化保證每次youngGC達到此指標,而非一定達到。

以上兩個配置,會影響到gc的頻率和時間,在某些場景下可能要進行鍼對性優化,接下來分別進行演示。

實踐MaxGCPauseMillis

#代碼準備
for (int i = 0; i < 50; i++) {
	//分配 10MB 堆空間
	byte[] b1 = new byte[1024 * 1024 * 10];
	byte[] b2 = new byte[1024 * 1024 * 10];
	LOG.info("初始化對象...");
}

#運行程序
nohup java -Xms4096M -Xmx4096M -jar microservice-web-1.0-SNAPSHOT.jar &

使用jmter進行壓測,100個請求共進行了:87次youngGC,共耗時7.331,平均84ms;25次full GC,共耗時6.008,平均240ms

#再次運行程序,將MaxGCPauseMillis設置爲40ms
nohup java -Xms4096M -Xmx4096M -XX:MaxGCPauseMillis=40 -jar microservice-web-1.0-SNAPSHOT.jar &

使用jmter進行壓測,100個請求共進行了:197次youngGC,共耗時14.441,平均73ms;16次full GC,共耗時3.908,平均244ms

接下來對比兩次堆內存分配情況

通過以上對比,發現MaxGCPauseMillis參數的確會影響年輕代中eden和Survivor空間配比;當MaxGCPauseMillis設置更小,爲保證年輕代回收儘可能達到預期值,eden區佔比就會更小,同時survivor區會增大,對象進入年老代次數會降低,此時FULL GC次數會下降。

結論:雖然youngGC和fullGC都會STW,但是fullGC會對整個堆空間進行回收,耗時時間較長,所以在適當的情況下,可以考慮調整此參數。

實踐動態調整策略UseAdaptiveSizePolicy

#運行程序,除了堆空間外不進行任何其他設置
nohup java -Xms4096M -Xmx4096M -jar microservice-web-1.0-SNAPSHOT.jar &

我們分別在未進行使用、和100個請求壓測後分別導出堆棧情況觀察

根據上圖結果可以看到,在默認開啓動態調整策略情況下,初始啓動後,eden和survivor分配佔比可以保持默認;但是隨着使用時間增加,survivor區在某些情況下會變的很小,這可能會直接導致大對象分配到老年代造成頻繁Full GC。

結論:在某些情況下,如果應用程序出現這種情況,可以考慮關閉動態調整策略。

#運行程序,關閉動態調整策略
nohup java -Xms4096M -Xmx4096M -XX:-UseAdaptiveSizePolicy -jar microservice-web-1.0-SNAPSHOT.jar &

我們可以在程序啓動、或者壓測過程中進行堆空間的對比;結果是新生代各分區佔比會保持默認值固定不變。

如果我們關閉動態調整策略,而eden和survivor佔比不符合預期,我們可以調整:SurvivorRatio參數:

nohup java -Xms4096M -Xmx4096M -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=10 -jar microservice-web-1.0-SNAPSHOT.jar &
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章