深刻理解JVM(③)經典的垃圾收集器

前言

若是說垃圾收集算法是內存回收的方法論,那垃圾收集器就是內存回收的實踐者。本次要介紹的是幾款「經典」的垃圾收集器,之因此被稱之爲「經典」,是爲了與幾款目前仍處於實驗狀態,可是執行效果上喲革命性改進的高性能低延遲收集器區分開來,雖然算不上最早進的技術,但倒是在實踐中千錘百煉,足夠成熟,能夠在商用生產環境上放心使用的所有垃圾收集器。
這些「經典」收集器之間的關係圖
經典收集器關係
這七種做用於不一樣分代的收集器,若是兩個之間存在連續,說明能夠搭配使用。目前這些垃圾收集器並不都是「萬能」的,因此針對於各個垃圾收集器,咱們的目的是根據本身的具體場景而去選擇合適的收集器。算法

Serial收集器

Serial收集器是最基礎、歷史最悠久的收集器,曾經(在JDK1.3.1以前)是HotSpot虛擬機新生代收集器的惟一選擇。這個收集器是一個單線程工做的收集器,在進行垃圾收集時,必須暫停其餘全部工做現場,直到它收集結束。
Serial/Serial Old收集器的運行過程。
Serial/Serial Old 收集器運行示意圖
對於這個收集器,雖然是單線程而且須要在暫停其餘全部工做線程。但這個收集器卻並非已經被拋棄或是過期的,由於它有着優於其餘收集器的地方,那就是簡單高效(與其餘收集器的單線程相比)。對於內存資源受限的環境,它是全部收集器裏額外內存消耗最小的;對於單核處理器或處理器核心數較少的環境來講,Serial收集器因爲沒有線程交互的開銷,專心作垃圾收集自如能夠得到最高的線程收集效率。多線程

ParNew

ParNew收集器實質上Serial收集器的多線程並行版本,除了同時使用多條線程進行垃圾收集以外,其他的行爲包括Serial收集器可用的全部控制參數、收集算法、Stop The World、對象分配規則、回收策略等都與Serial收集器徹底一致,在實現上也是共用了至關多的代碼。
ParNew收集器的工做過程示意圖:
ParNew/Serial Old收集器運行示意圖
ParNew收集器除了支持多線程並行收集以外,其餘與Serial收集器相比並無太多創新之處。可是它有一個與功能、性能無關卻很重要的優點,除了Serial收集器外,目前只有它能與CMS收集器配合工做。可是隨着G1收集器的出現,從JKD9開始,官方已經再也不推薦ParNew和CMS這種組合了,因此ParNew也就慢慢開始退出歷史舞臺了。併發

Parallel Scavenge收集器

Parallel Scavenge收集器也是一款新生代收集器,它一樣是基於標記-複製算法實現的收集器,也是可以並行收集的多線程收集器。這款收集器的關注點和其餘收集器不一樣,其餘收集器的關注點是儘量的縮短用戶線程停頓時間,而Parallel Scavenge收集器的目標則是到達一個可控制的 吞吐量所謂吞吐量就是處理器用於運行用戶代碼的時間與處理器總消耗時間的比值
Parallel Scavenge收集器的運行示意圖
Parallel Scavenge收集器的運行示意圖
Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量,分別是控制最大攔擊收集停頓時間的-XX:MaxGCPauseMilis參數以及直接這是吞吐量大小的-XX:GCTimeRatio參數。
-XX:MaxGCPauseMilis參數運行的值是一個大於0的毫秒數,收集器將盡可能保證內存回收花費的時間不超過用戶設定值。
-XX:GCTimeRatio參數的值則應當是一個大於0小於100的整數,也就是垃圾收集時間佔總時間的比率,至關於吞吐量的倒數。
Parallel Scavenge收集器還有一個參數-XX:+UserAdaptiveSizePolicy值得咱們關注。這是一個開關參數,在被激活以後就不須要人工置頂Eden去與Survivor區的比率、以及晉升老年代對象大小等細節參數了。虛擬機會根據運行狀況自行調節。自適應調節策略也是Parallel Scavenge收集器區別於ParNew收集器的一個重要特性。佈局

Serial Old收集器

Serial Old收集器是Serial 收集器的老年代版本,也是一個單線程收集器,一樣適用標記-整理算法。這個收集器的主要意義也是提供客戶端模式下的HotSpot虛擬機使用。
Serial Old運行示意圖:
Serial Old運行示意圖
若是在服務端模式下,Serial Old收集器可能有兩種用途:一種是在JDK5以及以前的版本中與Parallel Scavengen收集器搭配使用,另一種就是做爲CMS收集器發送失敗時的後備預案,在併發收集發生Concurrent Mode Failure 時使用。性能

Parallel Old 收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,支出多線程併發收集,基於標記-整理算實現。這個收集器是在JDK6時才提供的,以前新生代選擇了Parallel Scavenge收集器,老年代只能選擇Serial Old(PS MarkSweep)收集器。
直到Parallel Old 收集器出現後,「吞吐量優先」收集器終於有了筆記名副其實的搭配組合,在注重吞吐量或者處理器資源較爲稀缺的場合,均可以優先考慮Parallel Scavenge加Parallel Old收集器這個組合。
Parallel Old收集器工做示意圖:
Parallel Old收集器運行示意圖spa

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。
CMS是基於標記-清除算法實現的,可是整個運做過程相對於前面幾種收集器來講要複雜一些,整個過程分四步驟:
一、初始標記(CMS initial mark)
二、併發標記(CMS concurrent mark)
三、從新標記(CMS remark)
四、併發清除(CMS concurrent sweep)
初始標記、從新標記這兩個步驟仍然須要中止用戶線程。線程

  • 初始標記僅僅是標記一些GC Roots能直接關聯到的對象,速度很快;
  • 併發標記階段就是從GC Roots的直接關聯對象開始遍歷整個對象圖的過程,過程較長,單不須要停頓用戶線程;
  • 而從新標記是爲了修正繽紛標記期間,因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄。
  • 最後的併發清除階段,是清理掉標記階段判斷的已經死亡的對象 ,因爲不須要移動存活的對象,因此這個階段也是不須要停頓用戶線程的。
    因爲整個過程當中,最耗時的併發標記和併發清除階段,垃圾收集器的線程均可以與用戶線程一塊兒工做,因此總體來講,CMS收集器的整個回收過程是與用戶線程一塊兒併發執行的。
    下面是CMS收集器的運行示意圖:
    CMS收集器運行示意圖
    CMS是一款優秀的垃圾收集器,最主要的優勢就是:併發收集、低停頓
    可是它也是有三個明星的缺點的:
    一、 CMS收集器對處理器資源很是敏感。在併發階段,它雖然不會致使用戶線程停頓,但卻會由於佔用了一部分線程(或是處理器計算能力)而致使應用程序變慢,下降總吞吐量。
    二、因爲CMS沒法處理「浮動垃圾」,有可能出現內存不足致使另外一次徹底「Stop The World」的Full GC的產生。
    浮動垃圾是指:在CMS的併發標記和併發清理階段,用戶線程是孩子繼續進行的,程序在運行天然就還會伴隨有新的垃圾對象不斷產生,但這一部分垃圾對象是出如今標記過程結束之後,CMS沒法在當次收集中處理掉它們,只好留待下一次垃圾收集時再清理掉。這部分垃圾就稱爲「浮動垃圾」。
    三、由於CMS是基於「標記-清除」收集算法的,因此在收集結束後會有大量的碎片空間產生。這就會致使若是建立大對象的時候找不到連續的空間而而提早觸發一次Full GC的狀況。

Garbage First 收集器

Garbage First(簡稱G1)收集器是垃圾收集器技術發展歷史上的里程碑是的成果,它開創了收集器面向局部收集的設計思路和基於Region的內存佈局形式。
G1是一款主要面向服務端應的垃圾收集器。HotSpot開發團隊最初賦予它的指望是替換CMS收集器。從JDK9發佈之日,G1宣告取代Parallel Scavenge 加Parallel Old組合,稱爲服務端模式下的默認的垃圾收集器。
G1開創了基於Region的堆內存佈局是它可以實現能夠由用戶指定用戶線程停頓時間的關鍵。雖然G1也遵循分代收集的設計理論,但卻再也不堅持固定大小以及固定數量的分代區域劃分,而是把連續的Java堆劃分爲多個大小相等的獨立區域(Region),每個Region採用不一樣的策略去處理,這樣無理是新建立的對象仍是已經存活了一段時間、熬過屢次收集的舊對象都能獲取很好的收集效果。
G1收集器的分區示意圖:
G1收集器Region分區示意圖
若是咱們不去計算用戶線程運行過程當中的動做,G1收集器的運做過程大體可劃分爲如下四個步驟:設計

  • 初始標記: 僅僅是標記一些GC Roots能直接關聯到的對象,而且修改TAMS指針的值,讓下一階段用戶線程併發運行時,能正確地在可用的Region中分配新的對象。
  • 併發標記: 從GC Roots開始對堆中對象進行可達性分析,遞歸掃描整個堆裏的對象圖,找出要回收的對象,與用戶線程併發執行。
  • 最終標記: 對用戶線程作另外一個短暫的暫停,用於處理併發階段結束後仍遺留下來的最後那少許的SATB記錄。
  • 篩選回收: 負責更新Region的統計數量,對各個Region進行回收價值和成本排序,根據用戶指望的停頓時間制定回收計劃,而後把決定回收的那一部分Region的存活對象複製到空的Region中,再清理掉整個舊的Region的所有空間。
    下面是G1收集器的運行示意圖
    GQ

最後結合上節說的垃圾收集算法總結一下:指針

垃圾收集算法 垃圾收集器
標記-清除 CMS
標記-複製 Serial ParNew Parallel Scavenge
標記-整理 Serial Old Parallel Old G1
相關文章
相關標籤/搜索