垃圾回收器
- GC分類及性能名額
- 常見的垃圾回收器
- 新生代垃圾收集器
-
- serial收集器
- ParNew收集器
- Parallel Scavenge收集器
- 老年代垃圾收集器
-
- serial old垃圾收集器
- Parallel Old收集器
- CMS收集器
- 新生代和老年代收集器
-
- G1收集器
-
- G1收集器的運作過程:
本文是記錄在尚矽谷學習java的一些筆記,因為剛開始寫部落格,可能大部分都是PPT的搬用,希望多多包涵,後續會繼續更新,歡迎大家交流學習,如有不妥之處,歡迎多多評論
GC分類及性能名額

常見的垃圾回收器
新生代收集器: Serial、ParNew、Parallel Scavenge
老年代收集器: Serial Old、CMS、Parallel Old
新生代和老年代收集器: G1、ZGC、Shenandoah
新生代垃圾收集器
serial收集器
根據上圖中我們可以知道,當Serial收集器運作的時候,會暫停所有線程,“Stop The World” ,等到GC完成後,應用線程繼續執行,就類似于 你有三個女朋友,他們同時讓你陪他們去逛街,你隻能陪完其中一個才能去陪另外一個,陪其中一個的時候,其他女朋友就要等待,但是垃圾收集這項工作要比這種情況要複雜的多!
優勢: 因為使用的是單線程的方式,是以對于單個CPU來說,是其他類型收集器中效率最高的一個
缺點: 在使用者不可知、不可控的情況下,暫停所有線程,風險性和體驗感不好,讓人比較難接受
使用指令:可以開啟Serial 作為新生代收集器
-XX:+UserSerialGC #選擇Serial作為新生代垃圾收集器
ParNew收集器
ParNew收集器實質上是Serial收集器的多線程并行版本,除了同時使用多條線程進行垃圾收集器之外,其餘的比如Serial收集器可用的控制參數、收集算法、Stop The Wrold 、對象配置設定規則等等都和Serial收集器完全一樣,在多核機器上,預設開啟的手機線程數和CPU數量一樣,但是可以通過參數進行修改
-XX:ParallelGCThreads #設定JVM垃圾收集的線程數
ParNew收集器除了支援多線程并行收集之外,其他與Serial收集器相比并沒有太多創新之處,但它 卻是不少運作在服務端模式下的HotSpot虛拟機,尤其是JDK 7之前的遺留系統中首選的新生代收集 器,其中有一個與功能、性能無關但其實很重要的原因是:除了Serial收集器外,目前隻有它能與CMS 收集器配合工作。
優點:随着CPU的有效利用,對于GC時系統資源的有效利用有好處
缺點:同Serial一樣的毛病
使用場景:ParNew是許多運作在Server模式下的虛拟機中首選的新生代收集器,因為CMS隻能與Serial 或者 ParNew 配合使用,在如今的多核環境下,首選的是多線程并行的ParNew,ParNew收集器是激活CMS後(使用-XX:+UseConcMarkSweepGC選項)的預設新生代收集器,也可以使用-XX:+/-UseParNewGC選項來強制指定或者禁用它。
Parallel Scavenge收集器
Parallel Scavenge收集器也是一款新生代的收集器,它同樣是基于标記-複制算法那實作的收集器,也是能夠并行收集器的多線程收集器,Parallel Scavenge收集器關注點與其他收集器的不用處在于,CMS等收集器的關注點是盡可能地縮短垃圾收集時使用者線程的停頓時間,而Parallel Scavenge收集器的目标則是一個可控制的吞吐量,所謂的吞吐量就是處理器用于運作使用者代碼的時間與處理器總消耗的比值,如下圖所示:
如果說虛拟機完成某個任務,使用者代碼加上垃圾收集總共耗費了100分鐘,其中垃圾收集花掉1分鐘,那麼吞吐量就是99%。停頓時間越短就越适合需要與使用者互動或者需要保證服務響應品質的程式,良好的響應速度能提升使用者體驗。
垃圾收集器每100秒收集一次,每次停頓10秒,和垃圾收集器每50秒收集一次,每次停頓時間7秒,雖然後者停頓時間變短了,但是總體吞吐量變低了,CPU總體使用率變低了。
可以通過 -XX:MaxGCPauseMillis來設定收集器盡可能在多長時間内完成記憶體回收,可以通過 -XX:GCTimeRatio來精确控制吞吐量。
如下是 Parallel 收集器和 Parallel Old 收集器結合進行垃圾收集的示意圖,在新生代,當使用者線程都執行到安全點時,所有線程暫停執行,ParNew 收集器以多線程,采用複制算法進行垃圾收集工作,收集完之後,使用者線程繼續開始執行;在老年代,當使用者線程都執行到安全點時,所有線程暫停執行,Parallel Old 收集器以多線程,采用标記整理算法進行垃圾收集工作。
老年代垃圾收集器
serial old垃圾收集器
Serial Old 是 Serial收集器的老年代版本,它同樣是一個單線程收集器,使用标記-整理算法,這個收集器的主要意義也是供用戶端模式下HotSpot虛拟機使用。如果在服務端一種是與Parallel Scavenge收集器搭配使用,另外一種是作為CMS 收集器發生失敗時的後備預案。
Serial收集器與Serial Old收集器的運作示意圖:
适用場景: Client模式;單核伺服器;與Parallel Scavenge收集器搭配;作為CMS收集器的後備方案,在并發收集發生Concurrent Mode Failure時使用
Parallel Old收集器
Parallel Old 是 Parallel Scavenge收集器的老年代版本,支援多線程并發收集,基于标記-整理算法實作,可以充分利用多核CPU的計算能力,慮Parallel Scavenge/Parallel Old收集器運作示意圖:
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以擷取最短回收停頓時間為目标的收集器。CMS收集器是基于标記-清楚算法實作的,這個收集器的運作過程比前面的幾個收集器更複雜一點,整個過程分為四個步驟:
- 初始标記(CMS initial mark): 隻是标記 GC Roots能夠直接關聯到的對象,速度很快
- 并發标記(CMS concurrent mark): 從GC Roots 的直接關聯對象開始周遊整個對象圖的過程,這個過程耗時較長但是不需要停頓使用者線程,可以和垃圾收集線程一起并發運作
- 重新标記(CMS remark): 修正并發标記期間,因使用者程式繼續運作導緻标記産生對象的标記記錄,這個階段的停頓時間會比初始标記階段稍長一些
-
并發清除(CMS concurrent sweep): 清理删除掉标記階段判斷的已經死亡的對象,由于不需要移動存活對象,這個階段也是可以與使用者線程同時并發的。
其中 初始标記、重新标記這兩個步驟仍然需要 “Stop The World” 暫停所有使用者線程,由于在整個過程中耗時最長的并發标記和并發清理階段中,垃圾收集器線程都可以與使用者線程一起工作,總體來說,CMS收集器的記憶體回收過程是和使用者線程一起并發執行的,如下圖所示
16_垃圾回收器GC分類及性能名額常見的垃圾回收器新生代垃圾收集器老年代垃圾收集器新生代和老年代收集器 優點: CMS收集器是一款優秀的收集器,它主要展現為:并發收集、低停頓。
缺點:CMS收集器對處理器資源非常敏感,在并發階段,雖然不會導緻使用者線程停頓,但也會因為占用一部分線程導緻應用程式變慢,降級總的吞吐量。CMS預設啟動回收線程數是(處理器核心數量+3)/4,也就是說如果處理器核心數大于等于四個,并發回收時垃圾收集線程隻占用不超過25%的處理器運算資源,處理器資源會随着CPU數量的增加而下降,但是當CPU數量不足四個的時候,CMS對使用者程式的影響就可能變的很大。
CMS收集器無法處理 “浮動垃圾” ,有可能出現 “Concurrent Mode Failure” 失敗進而導緻另一次完全“Stop The World” 的Full GC 的産生,在CMS的并發标記和并發清理階段,使用者線程是還在繼續進行的,程式在運作自然就還會伴随有新的垃圾對象不斷産生,但這一部分垃圾對象是出現在标記過程結束以後,CMS無法在當次收集中處理掉它們,隻好留待下一次垃圾收集時再清理掉。這一部分的垃圾就稱為“浮動垃圾”
因為CMS是一款基于 “标記-清除”算法實作的收集器,是以收集結束時會有大量的空間碎片産生,空間碎片過多的時,将對給大對象帶來很大的麻煩,有可能不得不提前進行Full GC的操作,不過通過參數:-XX:+UseCMS-CompactAtFullCollection進行優化
新生代和老年代收集器
G1收集器
Garbage First (簡稱G1)收集器是垃圾收集器技術發展曆史上的裡程碑式的成果,它開創了收集器面向局部收集的設計思路和基于Region的記憶體布局形式。
G1收集器是一款面向伺服器端應用的垃圾收集器,在JDK9釋出的時候成為服務端模式下的預設垃圾收集器,而CMS則淪為不被推薦使用的收集器
特點:
在G1收集器出現之前所有的其他收集器,目标範圍要麼是新生代要麼是老年代,要麼就是Java堆,但是G1做了全面性,它可以面向堆記憶體任何部分來組成回收集進行回收,衡量标準不再是它屬于哪個分代,而是那塊記憶體中存放的垃圾數量最多,回收收益最大,這就是G1收集器的Mixed GC模式。而G1開創的基于Region的堆記憶體布局是它能夠實作這個目标的關鍵。
雖然G1仍然保留了新生代和老年代的概念,但新生代和老年代不再是固定的,他們都是一系列區域的動态集合,G1可以建立可預測的停頓時間模型,是因為它将Region作為單次回收最小單元
G1不再堅持固定大小以及固定數量的分代區域劃分,而是把連續的Java堆劃分為多個大小相等的獨立區域,每一個Region都可以根據需要,扮演新生代的Eden空間、Survivor空間或者老年代空間,收集器能夠對扮演不同的角色的Region采用不同的政策去處理。
Region中還有一類特殊的Humongous區域,專門用來存儲大對象。G1認為隻要大小超過一個Region容量一半的對象即可判定為大對象。
G1收集器的運作過程:
- 初始标記(Initial Marking): 标記GC Roots 能直接關聯到的對象,并且修改TAMS指針的值,讓下一階段使用者線程并發運作時,能正确在可用的Region中配置設定新對象,需要耗時較短的停頓線程,但是是借用Minor GC的時候同步完成的,是以在這個階段實際沒有額外的停頓
- 并發标記(Concurrent Marking): 從GC Roots 開始對堆中對象進行可達性分析,遞歸掃描整個堆裡面的對象圖,找出要回收的對象,這個階段耗時較長,但可以和使用者程式并發執行。
- 最終标記(Final Marking): 對使用者線程做另一個短暫的暫停,使用者處理并發階段結束後仍遺留下來的最後那少量的SATB記錄
- 篩選回收(Live Data Counting and Evacuation): 負責更新Region的統計資料,對各個Region的回收價值和成本進行排序,根據使用者鎖期望的停頓時間來制定回收計劃,可以隻有選擇任意多個Region構成回收集,然後把決定回收的那一部分Region的存活對象指派到空的Region中,再清理整個Region的全部空間。