垃圾收集器與內存分配策略(四)之垃圾收集器

垃圾收集器與內存分配策略(四)——垃圾收集器

 

收集算法是內存回收的方法論,垃圾收集器則是內存回收的具體實現。

 

垃圾收集器介紹

在垃圾收集器的層面上對並行與併發的解釋:
並行(Parallel):指多條垃圾收集線程並行工做,但此時用戶現場仍處於等待狀態。
併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但並不必定是並行的,可能會交替執行),用戶程序仍在繼續執行,而垃圾收集程序運行於另外一個CPU上。
對於不一樣的廠商,不一樣的版本的虛擬機均可能有很大的差異。此處討論的是jdk1.7以後的HotSpot虛擬機,以下圖所示:
enter description herehtml

Serial收集器

Serial/Serial Old收集器的運行過程以下圖所示:
enter description here
Serial收集器的特色:算法

  1. 最基本,發展歷史最悠久的收集器(jdk1.3.1以前是虛擬機新生代惟一的選擇)
  2. 是一個單線程的收集器,它的「單線程」不只僅指它只會使用一個CPU或一條收集線程去完成垃圾收集工做,更重要的是它進行垃圾收集時必須暫停其它全部的工做線程,直到它收集結束。
  3. 在用戶並不可見的狀況下把用戶正常工做的線程所有停掉,由虛擬機在後臺自動發起和自動完成。(固然隨着其它收集器的出現,用戶線程的停頓時間在不斷縮短,但仍然沒辦法徹底消除)

Serial收集器的優勢:
簡單而高效(與其餘收集器的單線程比),對於限定單個CPU環境來講,Serial沒有線程交互的開銷,專心作垃圾收集天然能夠得到最高的單線程收集效率。多線程

Serial收集器的特應用:
Serial是虛擬機運行在Client模式下的默認新生代收集器。併發

ParNew收集器

ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集以外,其他行爲與Serial收集器徹底同樣。
ParNew收集器的運行過程以下圖所示:
enter description here
ParNew收集器的特色:性能

  1. ParNew收集器除了多線程收集以外,其它與Serial收集器並無太多創新之處。
  2. ParNew收集器在單CPU的環境下絕對不會有比Serial收集器更好的效果。(甚至在二個CPU的環境中因爲線程交互的開銷,都不能100%保證超過Serial收集器)

ParNew收集器的應用:
運行在server模式下的虛擬機首選的新生代收集器。(一個與性能無關的重要緣由是除了Seria就只有ParNewl能與CMS收集器配合工做)優化

Parallel Scavenge收集器

Parallel Scavenge收集器是一個新生代、使用複製算法、並行的多線程收集器。網站

Parallel Scavenge收集器的特色:spa

  1. Parallel Scavenge收集器的目的是達到一個可控制的吞吐量(Throughput)而其餘收集器目的是儘量縮短垃圾收集時用戶線程的停頓時間(如CMS等收集器)。
  2. 「吞吐量優先」的收集器,吞吐量即運行用戶代碼的時間 / (垃圾收集時間+運行用戶代碼時間)。
  3. Parallel Scavenge收集器提供了二個參數用於精確控制吞吐量。
    控制最大垃圾收集時間:-XX:MaxGCPauseMillis(大於0的毫秒數)。經過調小新生代空間大小,提升垃圾收集頻率來使垃圾收集停頓時間降低,但同時也下降了吞吐量。
    直接設置吞吐量大小:-XX:GCTimeRatio(0-100的整數)。垃圾收集時間佔總時間的比率,至關於吞吐量的倒數,參數默認值爲99。如將參數設置爲19,則容許最大的垃圾收集時間佔總時間的1 / (1+ 19)即5%。
  4. GC自適應調節策略:Parallel Scavenge收集器經過設置-XX:+UseAdaptiveSizePolicy這個開關參數,虛擬機經過當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量。
    設置步驟爲:先設置基本內存數據(如:-Xmx設置最大堆),而後給虛擬機一個優化目標MaxGCPauseMillis參數(關注最大垃圾收集停頓時間)或GCTimeRatio參數(關注吞吐量)便可,最後具體細節參數由虛擬機自動完成。

Parallel Scavenge收集器的適用場景:
高吞吐量則能夠高效的利用CPU時間,儘快完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務。線程

Serial Old收集器

Serial Old是一個單線程、使用」標記-整理「算法、Serial的老年代版本的收集器。Serial/Serial Old收集器的運行過程以下圖所示:
enter description here3d

Serial Old收集器的使用場景:

  1. Serial Old能夠在Client模式下的虛擬機使用
  2. Serial Old能夠與Parallel Scavenge搭配使用
  3. Serial Old能夠做爲CMS收集器的後備方案在併發收集時發生Concurrent Mode Failure時使用

Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。Parallel Old收集器的工做過程以下圖:
enter description here
Parallel Old收集器適用場景:注重吞吐量及CPU資源敏感的場合。

CMS收集器

在jdk1.5時期,HotSpot推出了CMS收集器,這是HotSpot虛擬機中第一款真正意義上的併發收集器,他第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工做。
CMS(Concurrent Mark Sweep):併發標誌清除收集器是一種以獲取最短回收停頓時間爲目標的收集器。工做過程以下圖:
enter description here

CMS收集器使用的是「標記—清除算法」,整個過程分爲4步:

  1. 初始標記(CMS initial mark):只標記GC roots能直接關聯到的對象,速度很快。
  2. 併發標記(CMS concurrent mark):進行GC Roots Tracing的過程。
  3. 從新標記(CMS remark):修正併發標記期間由於用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄。從新標記的標記時間比初始標記時間稍長,但遠比並發標記時間短。
  4. 併發清除(CMS concurrent sweep)

其中初始標記及從新標記仍然須要「stop the world」,但整體時間較短。而耗時較長的併發標記及併發清除能夠與用戶線程一塊兒工做。因此整體上來講CMS收集器的內存回收是與用戶線程一塊兒執行的。

CMS收集器的優勢:併發收集、低停頓。
CMS收集器的缺點:

  1. CMS收集器對CPU資源敏感,在併發階段雖然不會致使用戶線程停頓,可是由於佔用了一部分CPU資源會致使應用程序變慢,總吞吐量下降。
  2. CMS收集器沒法處理浮動垃圾(併發運行時,用戶程序運行時產生的垃圾),可能致使「Concurrent Model Failure」失敗而致使另外一次Full GC的產生。同時因爲內存回收線程與用戶線程同時運行,在垃圾回收時不能等老年代空間快滿了時進行回收,必須爲用戶線程的運行預留一部分空間。若預留的內存沒法知足用戶程序的須要就會出現「Concurrent Model Failure」失敗,這是虛擬機將臨時採用Serial Old收集器進行收集,致使停頓時間變很長。
  3. 空間碎片太多,給大對象分配空間時找不到足夠大的連續空間來分配當前對象,而提早觸發一次Full GC。爲了解決這個問題,提供了二個參數
    -XX:UseCMSCompactAtFullCollection開關參數(默認開啓),用於在頂不住要進行Full GC時進行內存碎片整理(不能併發,停頓時間變長);
    -XX:CMSFullGCsforeCompaction用於設置執行多少次不壓縮的Full GC後帶來一次壓縮的(默認爲0,表示每次進入Full GC都進行碎片整理)。

CMS的適用場景:
集中在互聯網站或者B/S系統的服務端上,重視服務的響應速度,以帶給用戶較好的體驗。

G1收集器

G1(Garbage-First)收集器是一款面向服務端應用的垃圾收集器。使用G1收集器時,它將Java堆分紅多個大小相等的獨立區域(Region),新生代與老年代不在物理相隔了,它們都是一部分Region(不須要連續)的集合。工做過程以下圖:
enter description here

G1收集器的特色:

  1. 並行與併發:充分利用多CPU、多核環境下的硬件優點來縮短「stop-the-world」的時間。
  2. 分代收集:不須要其餘收集器的配合,就能獨立管理整個GC堆,但也能採用不一樣的方式對不一樣階段的對象(新建立的、存活了一段時間的,通過屢次GC的舊對象)進行收集。
  3. 空間整合:總體基於「標記-整理」算法;局部(二個Region)之間採用「複製」算法。都不會產生內存空間碎片。
  4. 可預測的停頓:能讓使用者指定在一個長度爲M毫秒的時間片斷內,垃圾收集消耗的時間在N毫秒內。(之因此能創建可預測的時間停頓模型在於G1避免了在整個Java堆中進行全區域的垃圾回收;G1跟蹤每一個Region裏面的垃圾堆積的價值大小(時間、空間),維護一個優先列表,根據容許的收集時間優先選擇回收價值大的Region。)

爲了不由於Region之間存在引用,從而在進行可達性分析判斷對象是否存活時進行整個Java堆全堆掃描,G1中每一個Region都有一個對應的Remenbered Set來記錄區域之間對象的引用信息,它的工做過程以下:

  1. 虛擬機發現程序在對Reference進行寫操做時,產生一個Write Barrier暫時中斷寫操做
  2. 檢查Referrence引用的對象是否在不一樣的Region中(在分代收集中即判斷是否老年代的對象引用了新生代的對象)
  3. 若是是在不一樣的Region之間,便經過CardTable把相關的引用信息記錄到被引用對象所屬的Region的Remenbered Set中
  4. 進行內存回收時,在GC Roots枚舉範圍中中加入Remenbered Set便可保證不對全堆掃描也不會有遺漏

G1收集器的工做步驟(不考慮Remenbered Set的維護操做):

  1. 初始標記(Initial Marking):標記GC Roots能直接關聯到的對象,而且修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中建立新對象,這階段須要停頓線程,但耗時很短。
  2. 併發標記(Concurrent Marking):從GC Root開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序併發執行。
  3. 最終標記(Final Marking):爲了修正併發標記期間,因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程Remembered Set Logs裏面,最終標記階段須要把Remembered Set Logs的數據合併到Remembered Set中,這階段須要停頓線程,可是可並行執行。
  4. 篩選回收(LIve Data Counting and Evacuation):最後篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所指望的GC停頓時間來制定回收計劃,從Sun透露出來的信息來看,這個階段其實也能夠作到與用戶程序一塊兒併發執行,可是由於只回收一部分Region,時間是用戶可控制的,並且停頓用戶線程將大幅提升收集效率。
相關文章
相關標籤/搜索