咱們先回顧一下主流Java的垃圾回收器(HotSpot JVM)。本文是針對堆的垃圾回收展開討論的。html
堆被分解爲較小的三個部分。具體分爲:新生代、老年代、持久代。算法
絕大部分新生成的對象都放在Eden區,當Eden區將滿,JVM會因申請不到內存,而觸發Young GC ,進行Eden區+有對象的Survivor區(設爲S0區)垃圾回收,把存活的對象用複製算法拷貝到一個空的Survivor(S1)中,此時Eden區被清空,另一個Survivor S0也爲空。下次觸發Young GC回收Eden+S0,將存活對象拷貝到S1中。新生代垃圾回收簡單、粗暴、高效。多線程
若發現Survivor區滿了,則將這些對象拷貝到old區或者Survivor沒滿但某些對象足夠Old,也拷貝到Old區(每次Young GC都會使Survivor區存活對象值+1,直到閾值)。 3.Old區也會進行垃圾收集(Young GC),發生一次 Major GC 至少伴隨一次Young GC,通常比Young GC慢十倍以上。併發
JVM在Old區申請不到內存,會進行Full GC。Old區使用通常採用Concurrent-Mark–Sweep策略回收內存。spa
總結:Java垃圾回收器是一種「自適應的、分代的、中止—複製、標記-清掃」式的垃圾回收器。線程
缺點:orm
GC過程當中會出現STW(Stop-The-World),若Old區對象太多,STW耗費大量時間。htm
CMS收集器對CPU資源很敏感。對象
CMS收集器沒法處理浮動垃圾,可能出現「Concurrent Mode Failure」失敗而致使另外一次Full GC的產生。內存
CMS致使內存碎片問題。
在G1中,堆被劃分紅 許多個連續的區域(region)。每一個區域大小相等,在1M~32M之間。JVM最多支持2000個區域,可推算G1能支持的最大內存爲2000*32M=62.5G。區域(region)的大小在JVM初始化的時候決定,也能夠用-XX:G1HeapReginSize設置。
在G1中沒有物理上的Yong(Eden/Survivor)/Old Generation,它們是邏輯的,使用一些非連續的區域(Region)組成的。
新生代收集
G1的新生代收集跟ParNew相似,當新生代佔用達到必定比例的時候,開始出發收集。
被圈起的綠色部分爲新生代的區域(region),通過Young GC後存活的對象被複制到一個或者多個區域空閒中,這些被填充的區域將是新的新生代;當新生代對象的年齡(逃逸過一次Young GC年齡增長1)已經達到某個閾值(ParNew默認15),被複制到老年代的區域中。
回收過程是停頓的(STW,Stop-The-Word);回收完成以後根據Young GC的統計信息調整Eden和Survivor的大小,有助於合理利用內存,提升回收效率。
回收的過程多個回收線程併發收集。
老年代收集
和CMS相似,G1收集器收集老年代對象會有短暫停頓。
標記階段,首先初始標記(Initial-Mark),這個階段是停頓的(Stop the World Event),而且會觸發一次普通Mintor GC。對應GC log:GC pause (young) (inital-mark)
Root Region Scanning,程序運行過程當中會回收survivor區(存活到老年代),這一過程必須在young GC以前完成。
Concurrent Marking,在整個堆中進行併發標記(和應用程序併發執行),此過程可能被young GC中斷。在併發標記階段,若發現區域對象中的全部對象都是垃圾,那個這個區域會被當即回收(圖中打X)。同時,併發標記過程當中,會計算每一個區域的對象活性(區域中存活對象的比例)。
Remark, 再標記,會有短暫停頓(STW)。再標記階段是用來收集 併發標記階段 產生新的垃圾(併發階段和應用程序一同運行);G1中採用了比CMS更快的初始快照算法:snapshot-at-the-beginning (SATB)。
Copy/Clean up,多線程清除失活對象,會有STW。G1將回收區域的存活對象拷貝到新區域,清除Remember Sets,併發清空回收區域並把它返回到空閒區域鏈表中。
複製/清除過程後。回收區域的活性對象已經被集中回收到深藍色和深綠色區域。
關於Remembered Set概念:G1收集器中,Region之間的對象引用以及其餘收集器中的新生代和老年代之間的對象引用是使用Remembered Set來避免掃描全堆。G1中每一個Region都有一個與之對應的Remembered Set,虛擬機發現程序對Reference類型數據進行寫操做時,會產生一個Write Barrier暫時中斷寫操做,檢查Reference引用的對象是否處於不一樣的Region之間(在分代中例子中就是檢查是否老年代中的對象引用了新生代的對象),若是是便經過CardTable把相關引用信息記錄到被引用對象所屬的Region的Remembered Set中。當內存回收時,在GC根節點的枚舉範圍加入Remembered Set便可保證不對全局堆掃描也不會有遺漏。
G1雖然保留了CMS關於代的概念,可是代已經不是物理上連續區域,而是一個邏輯的概念。在標記過程當中,每一個區域的對象活性都被計算,在回收時候,就能夠根據用戶設置的停頓時間,選擇活性較低的區域收集,這樣既能保證垃圾回收,又能保證停頓時間,並且也不會下降太多的吞吐量。Remark階段新算法的運用,以及收集過程當中的壓縮,都彌補了CMS不足。引用Oracle官網的一句話:「G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS)」。