CMS、G1收集器

CMS、G1收集器

一、CMS收集器

CMS(Concurrent Mark Sweep) 收集器是一種以獲取最短回收停頓時間爲目標的收集器。優勢是併發收集、低停頓。目前很大一部分的Java 應用集中在互聯網站或者B/S系統的服務端上,這類應用尤爲重視服務的響應速度,但願系統停頓時間最短,以給用戶帶來較好的體驗。CMS收集器就很是符合這類應用的需求。算法

1.一、原理

CMS收集器基於「標記—清除」算法實現,整個過程分爲四個步驟:併發

  1. 初始標記(initial mark)
  2. 併發標記(concurrent mark)
  3. 從新標記(remark)
  4. 併發清除(concurrent sweep)

其中,初始標記、從新標記這兩個步驟仍然須要Stop-The-World。初始標記僅僅只是標記一下GC Roots 能直接關聯到的對象,速度很快,併發標記階段就是進行Gc Roots Tracing的過程。而從新標記階段則是爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄。因爲整個過程當中耗時最長的併發標記和併發清除過程收集器線程均可以與用戶線程一塊兒工做,因此,從整體上來講,CMS 收集器的內存回收過程是與用戶線程一塊兒併發執行的。佈局

1.二、不足

(1)對CPU 資源很是敏感。性能

其實, 面向併發設計的程序都對 CPU 資源比較敏感。在併發階段,它雖然不會致使用戶線程停頓, 可是會由於佔用了一部分線程(或者說 CPU 資源)而致使應用程序變慢,總吞吐量會下降。CMS 默認啓動的回收線程數是 (CPU 數量+3) /4, 也就是當 CPU 在 4 個以上時, 併發回收時垃圾收集線程很多於25% 的CPU資源,而且隨着 CPU 數量的增長而降低。可是當 CPU 不足4個(好比 2 個)時,CMS 對用戶程序的影響就可能變得很大,若是原本CPU負載就比較大,還分出一半的運算能力去執行收集器線程,就可能致使用戶程序的執行速度突然下降了 50%,讓人沒法接受。網站

(2)沒法處理浮動垃圾(Floating Garbage),可能出現「Concurrent Mode Failure」失敗而致使另外一次Full GC的產生。線程

因爲CMS併發清理階段用戶線程還在運行着,伴隨程序運行天然就還會有新的垃圾不斷產生,這一部分垃圾出如今標記過程以後,CMS沒法在當次收集中處理掉它們,只好留待下一次GC時再清理掉。這一部分垃圾就稱爲「浮動垃圾」。也是因爲在垃圾收集階段用戶線程還須要運行,那也就還須要預留有足夠的內存空間給用戶線程使用,所以CMS收集器不能像其餘收集器那樣等到老年代幾乎徹底被填滿了再進行收集,須要預留一部分空間提供併發收集時的程序運做使用。設計

在JDK1.5的默認設置下,CMS收集器當老年代使用了68%的空間後就會被激活,這是一個偏保守的設置,若是在應用中老年代增加不是太快, 能夠適當調高參數-XX:CMSInitiatingOccupancyFraction的值來提升觸發百分比,以便下降內存回收次數從而獲取更好的性能,在JDK1.6中,CMS收集器的啓動閾值已經提高至92%。要是CMS運行期間預留的內存沒法知足程序須要,就會出現一次 "Concurrent Mode Failure"失敗,這時虛擬機將啓動後備預案:臨時啓用SerialOld 收集器來從新進行老年代的垃圾收集,這樣停頓時間就很長了。因此說參數-XX:CMSlnitiatingOccupancyFraction設置得過高很容易致使大量"Concurrent Mode Failure" 失敗,性能反而下降。3d

(3)空間碎片的產生。對象

由於是採用」標記—清除」算法,意味着收集結束時會有大量空間碎片產生。空間碎片過多時,將會給大對象分配帶來很大麻煩,每每會出現老年代還有很大空間剩餘,可是沒法找到足夠大的連續空間來分配當前對象,不得不提早觸發一次Full GC。blog

爲了解決這個問題,CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection開關參數(默認就是開啓的),用於在CMS收集器頂不住要進行Full GC時開啓內存碎片的合併整理過程,內存整理的過程是沒法併發 的,空間碎片問題沒有了,但停頓時間不得不變長。虛擬機設計者還提供了另一個參數-XX:CMSFullGCsBeforeCompaction,這個參數是用於設置執行多少次不壓縮的 Full GC後,跟着來一次帶壓縮的(默認值爲0,表示每次進人Full GC時都進行碎片整理)。

二、G1收集器

2.一、特色

G1(Garbage-Firsts,優先處理價值大的內存塊)收集器是當今收集器技術發展的最前沿成果之一,而且還在不斷髮展、完善,與其餘GC收集器相比G1具有以下特色:

  • 並行與併發:G1能充分利用多CPU、多核環境下的硬件優點,使用多個CPU(CPU 或者CPU核心)來縮短Stop-The-World停頓的時問,部分其餘收集器本來須要停頓 Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓JAVA程序繼續執行
  • 分代收集:與其餘收集器同樣,分代概念在G1中依然得以保留。雖然G1能夠不須要其餘收集器配合就能獨立管理整個GC堆,但它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間、熬過屢次GC的舊對象以獲取更好的收集效果。
  • 空間整合:與CMS的「標記一清理」算法不一樣,G1從總體來看是基於「標記—整理」算法實現的收集器,從局部(兩個Region之間)上來看是基於「複製」算法實現的,但不管如何,這兩種算法都意味着G1運做期間不會產生內存空間碎片,收集後能提供規整的可用內存。這種特性有利於程序長時間運行,分配大對象時不會由於沒法找到連續內存空間而提早觸發下一次GC。
  • 可預測的停頓:這是G1相對於CMS的另外一大優點,下降停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M亳秒的時間片斷內,消耗在垃圾收集上的時間不得超過N亳秒,這幾乎已是實時Java(RTSJ)的垃圾收集器的特徵了。

在G1以前的其餘收集器進行收集的範圍都是整個新生代或者老年代,而G1再也不是這樣。使用G1收集器時,Java堆的內存佈局就與其餘收集器有很大差異,它將整個Java堆劃分爲多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代再也不是物理隔離的了,它們都是一部分Region(不須要連續)的集合。

G1收集器之因此能創建可預測的停頓時間模型,是由於它能夠有計劃地避免在整個Java堆中進行全區域的垃圾收集。G1 跟蹤各個Region裏面的垃圾堆積的價值大小(回收所得到的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region (這也就是 Garbage-First 名稱的來由)。這種使用Region劃份內存空間以及有優先級的區域回收方式,使得G1收集器在有限的時間內能夠獲取儘量高的收集效率。

2.二、執行過程

G1收集器的運做大體可劃分爲如下幾個步驟:

  • 初始標記 (Initial Marking)
  • 併發標記 (Concurrent Marking)
  • 最終標記 (Final Marking)
  • 篩選回收 (Live Data Counting and Evacuation)

初始標記階段僅僅只是標記一下GC Roots能直接關聯到的對象,而且修改NTAMS(Next Top at Mark Start)的值,讓下一階段用戶程序併發運行時,能在正確可用的Region中建立新對象,這一階段須要停頓線程,但耗時很短。併發標記階段是從GC Roots開始對堆中的對象進行可達性分析,找出存活的對象,這一階段耗時較長,但可與用戶程序併發執行。而最終標記階段是爲了修正在併發標記期間因用戶程序繼續運行而致使標記產生變更的那一部分標記記錄,這一階段須要停頓線程,可是可並行執行。最後在篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所指望的GC停頓時間來制定回收計劃。


若是有學到東西,請點贊給予鼓勵,謝謝。

相關文章
相關標籤/搜索