1.回顧CMS
1.1堆內存結構
新生代分為Eden區和兩個survivor區。老年代是一塊連續區域。
只有FullGC時才可能發生內存整理。
1.2新生代GC
新生代淡綠色,老年代藍色。系統運行一段時間后CMS堆內存可能如下圖所示,對象分散在老年代各處。
新生代存活對象從Eden區和survivor區復制到另一個空閑的survivor區。任何minorGC年齡達到閾值的老對象被升級至老年代。
youngGC(minorGC)后,Eden區和一個survivor區被清空。
新近升級至老年代的對象以深藍色表示。綠色對象是新生代仍存活的對象。
1.3老年代GC
在初始標記和重新標記階段發生STW。當老年代剩余空間達到閾值時(發生Concurrent Mode Failure),使用SerialOld替代CMS進行老年代GC。
(1)初始標記停頓時間很短,簡單的標記GCRoot引用的對象。(2)並發標記在標記存活對象時,應用繼續執行。(3)重新標記階段,標記並發階段遺漏的存活對象。
未被標記的對象直接被回收。完成並發清掃后,許多老年代空間被釋放出來。同時內存整理仍沒有發生,老年代存在大量內存碎片。
最終,CMS經過重置階段,等待下一次GC。
2.G1收集器
2.1G1實現概覽及使用場景
G1收集器將java堆均分成大小相同的區域(region,1M-32M,最多2000個,最大支持堆內存64G)。一個或多個不連續的區域共同組成eden、survivor或old區,但大小不再固定,這為內存應用提供了極大地彈性。G1垃圾收集過程與CMS類似。G1在堆內存中並發地對所有對象進行標記,決定對象的可達性。經過全局標記,G1了解哪些區域幾乎是空的,然后優先收集這些區域,這就是GarbageFirst的命名由來。G1將垃圾收集和內存整理活動專注於那些幾乎全是垃圾的區域,並建立停頓預測模型來決定每次GC時回收哪些區域,以滿足用戶設定的停頓時間。
對於區域的回收通過復制算法實現。在完成標記清理后,G1將這幾個區域的存活對象復制到一個單獨區域中,實現內存整理和空間釋放。這一過程通過多線程並行進行來降低停頓時間,提高吞吐量。通過這樣的方式,G1在每次GC過程中持續清理碎片,控制停頓時間滿足用戶要求。這時過去虛擬機無法做到的。CMS不清理內存碎片(除非通過虛擬機參數設置,在每次或多次FullGC后進行整理),ParallelOld進行全堆整理,會導致較長的停頓時間。
G1不是實時垃圾收集器,它會盡量讓停頓時間低於用戶設置的停頓時間目標但不能保證一定如此。G1根據歷史垃圾收集監測數據來 預測每個區域的回收時間,然后根據用戶設定的目標停頓時間決定每次GC時可以回收哪些區域。G1通過這種方式建立比較精確的區域回收時間預測模型。
G1的推薦使用場景
G1的設計初衷是為用戶提供大內存、低GC停頓時間的應用解決方案。這意味着堆內存6G或更大,停頓時間穩定且少於0.5秒。
如果應用正在使用CMS或ParallelOld且面臨以下問題,推薦將應用遷移至G1
- FullGC發生頻繁或總時間過長
- 對象分配率或對象升級至老年代的比例波動較大
- 較長的垃圾收集或內存整理停頓(大於0.5至1秒)
注意:如果應用沒有上述問題,不需要遷移虛擬機。G1並不是最新JDK要求的虛擬機。
2.2GC
2.2.1新生代GC
存活對象移動到一個或多個survivor區域。如果對象達到晉升年齡,將被移動到老年代區域。
新生代GC總結:
-
新生代垃圾回收(youngGC)需要STW,所有應用線程需要停頓。
-
youngGC多線程並行執行。
-
存活對象復制到新的survivor區或老年代區。
2.2.2老年代GC
階段
|
描述
|
說明
|
---|---|---|
初始標記 (Stop the world) |
標記GC Roots直接引用的對象,新生代直接引用的老年代對象。
|
|
並發標記 | 標記堆中所有存活的對象。與用戶的應用程序並發執行。 在並發標記階段,若發現區域對象中的所有對象都是垃圾,那個這個區域會在重新標記階段被立即回收。 同時,並發標記過程中,會計算每個區域的對象活性(區域中存活對象的比例)。 |
|
重新標記 (Stop the world) |
修正並發標記階段因用戶程序繼續運行而導致標記發生變動的那一部分標記記錄 如果發現完全沒有活對象的region就會將其回收到空閑列表。 |
|
清除 (Stop the world) |
清點和重置階段。 1)使用marking bitmap統計每個region被標記為活的對象有多少,統計每個區域的對象活性(區域中存活對象的比例)。 2)重置Remembered Sets |
|
復制 (Stop the world) |
將存活的對象復制到未使用的region中。 G1選擇那些活躍度最低,回收速度最快的區域進行回收。 |
|
老年代GC總結
- 並發標記階段
- 區域活躍度信息的統計與應用線程並發進行
- 活躍度信息決定了那個區域最先在清理階段被回收
- 沒有CMS的清理階段
- 再次標記階段
- SATB算法比CMS使用的算法更快
- 空區域在這個階段被回收
- 復制/清理階段
- 新生代和老年代同時被回收
- 老年代根據活躍度確定回收優先級
2.2.3 其他
Remembered Set(可認為是GC Roots的補充)
每一個Region都有一個對應的Remembered Set,里面記錄了所有來自外部的引用,這些引用將被認為是GC roots的補充。
2.4 最佳實踐
2.4.1 JVM參數
基本參數設置
以下是使用G1收集器的javademo演示
java -Xmx50m -Xms50m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar
關鍵參數設置
- -XX:+UseG1GC - 啟用G1
- -XX:MaxGCPauseMillis=200 - GC停頓的最長時間。這是一個軟目標,即虛擬機會盡最大可能滿足這一時間,但某些情況下仍可能超過。默認值為200ms。
- -XX:InitiatingHeapOccupancyPercent=45 - 堆內存使用率達到多少時啟動一次GC過程。GC過程涉及整個堆內存,不單指某個年齡代。設為0時表示循環進行並發GC。默認值為45,即堆內存使用45%后觸發一次GC。
3.參考資料
http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html Getting Started with the G1 Garbage Collector
https://www.cnblogs.com/oldtrafford/p/6883796.html Getting Started with the G1 Garbage Collector(中文)
https://www.jianshu.com/p/74dd0ffd4386?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation Java垃圾回收手冊(四):垃圾回收算法實現
http://blog.jobbole.com/109170/ 深入理解Java G1垃圾收集器
http://hllvm.group.iteye.com/group/topic/44381 高級語言虛擬機論壇 [資料] [HotSpot VM] 請教G1算法的原理