1. 程式人生 > 其它 >G1垃圾回收器--基本知識及原理解析

G1垃圾回收器--基本知識及原理解析

G1介紹(Garbage first)
G1主要面向的是服務端的垃圾回收器。在G1之前,JVM的主要垃圾回收器採用的是物理分代的思想,將記憶體區域嚴格的劃分成年輕代(young GC)和老年代(major GC),然後針對於年輕代和老年代使用不同的垃圾回收器進行GC操作,直到G1,G1採用的是對整個堆進行回收,並且G1使用的分割槽region思想將記憶體劃分成了許多的分割槽。

雖說G1不使用嚴格將記憶體分為年輕代和老年代,但是在邏輯層面G1還是將分割槽region貼上了標籤Eden,Survivor,Old,也是一種分代的思想,並且G1針對於特別大的物件(當一個物件會佔據一個分割槽的一般以上空間G1稱為大物件)會將該大物件用一段連續的多個Humongous分割槽(專門存放大物件)存放。G1的大多數行為都會將Humougous分割槽判定為老年代看待。


每個region的大小可以通過XX:G1HeapRegionSize設定(取值範圍1~32MB),且為2的N次冪。
G1的停頓時間控制
使用者自定義停頓時間
G1的還有一個特點就是使用者可以通過設定-XX:MaxGCPauseMills來設定使用者允許的停頓時間(預設為200ms),G1會根據這個時間儘可能的回收垃圾,所以這也是G1是比較全能的原因。針對使用者自定義停頓時間我們可以猜想到,G1很可能無法完全回收所有垃圾(因為設定了一個時間),但是G1會盡可能的多收集垃圾。
有點類似於老闆給員工制定一個KPI,要求高就多收集點,要求低就少收集點。這也是G1的一個回收特點:並不是回收全部垃圾,而是儘可能在使用者規定得停頓時間內儘可能回收垃圾多的region。

G1如何實現儘可能回收最多垃圾region?
G1之所以能夠建立預測停頓時間的模型,依賴於G1是針對於分割槽region進行整體回收,即每次回收都是以region為單位。那麼G1如何判斷哪些region的回收率高(即垃圾多):G1收集器會跟蹤每個region裡面垃圾堆積的價值(即回收該region所獲的空間和所需時間的價值),然後再後臺維護一個優先順序列表,每次根據該優先順序列表進行回收(優先處理優先順序高的region),這也是Garbage First的由來。
如果存在跨代引用如何解決?
針對於年輕代的region我們每次都會進行回收,所以不需要考慮年輕代指向老年代。
針對於老年代指向年輕代的引用我們如何選擇(如果不管則使用整個堆的GC root掃描),這裡提出的思想是記憶集rset(remember set),

G1是對每個region維護了一個rset,記憶集中維護了指向自己的region的指標,並且標記指標分別在那些卡頁的範圍之內。由於G1使用了很多的region(每個region都有一個rset),所以G1使用了較高的記憶體,基本佔用Java對記憶體的10%~20%的額外記憶體來維持收集器的工作。
使用者停頓時間的設定

  • 如果使用者停頓時間過長:那麼會導致使用者執行緒停止很長時間,降低吞吐量。
  • 如果使用者停頓時間過短:那麼會導致一次GC回收不了太多的垃圾物件,則會導致頻繁GC,也會降低吞吐量。

G1垃圾收集的工作流程
G1的收集過程主要分為四步:
1.初始標記
僅僅標記GC roots能直接關聯到的物件。該步驟需要STW,但是時間極短。
2.併發標記
從GC roots使用可達性演算法,對堆記憶體中物件進行標記(三色標記法),遞迴掃描整個堆。該步驟耗時過長,是與使用者業務程式併發執行。
3.重新標記
對使用者執行緒短暫的STW,用於處理併發標記中物件的一些變更情況的最終確定。
4.篩選回收
G1會根據使用者規定的停頓時間,最大的回收region,將需要被回收的region複製到空白region中,再將原region全部回收。該步驟也需要STW,因為涉及到了物件的移動(一個region到另一個region)。

三色標記法

黑色:自身及其子物件已經被標記。
灰色:自身已經被標記,但是子物件沒有完全被標記
白色:自身未被標記,如果標記結束後仍然為白色,則是垃圾物件。

郭慕榮部落格園