閱讀416 返回首頁    go 微軟 go windows


Garbage First(G1)垃圾收集器

引言:G1垃圾收集器采用一個略微不同的手段來解決並行、串行以及CMS GC的眾多缺陷。對於大的Java堆來說,通過將Java堆拆分成一個個分區,G1會比其他垃圾收集器有更好的綜合表現。
本文選自《Java性能調優指南》。

  G1垃圾收集器采用一個略微不同的手段來解決並行、串行以及CMS GC的眾多缺陷。G1將堆拆成一係列的分區,這樣在一個時間段內,大部分的垃圾收集操作就隻是在一個分區內執行,而不是整個堆或整個(老年)代。

  在G1裏,年輕代就是一係列的內存分區,這意味著不用再要求年輕代是一個連續的內存塊。類似地,老年代同樣也是由一係列的分區組成。這樣也就不需要在JVM運行時考慮哪些分區是老年代,哪些是年輕代。事實上,G1通常的運行狀態是映射G1分區的虛擬內存隨著時間的推移在不同的代之間前後切換。一個G1分區最初被指定為年輕代,經過一次年輕代的回收之後,整個年輕代分區都被劃入到未被使用的分區中,那它也就可以被使用在別的地方了。

  一個可用分區能被用於或指定為年輕代或老年代分區。很可能在完成一個年輕代收集之後,一個年輕代的分區在未來的某個時刻被用於老年代分區。同樣地,在一個老年代分區完成收集之後,它就成為了可用分區,在未來某個時候作為一個年輕代分區來使用。

  G1年輕代的收集方式是並行stop-the-world。在垃圾收集線程執行過程中,並行stop-the-world回收將暫停所有Java應用線程,而垃圾回收的工作也將通過多個線程來分擔。與其他HotSpot垃圾收集器一樣,一旦發生一次年輕代垃圾收集,整個年輕代都會被回收。

  而G1老年代的垃圾收集方式與其他HotSpot垃圾收集器有著極大的不同。G1老年代的收集不會為了釋放老年代的空間就要求對整個老年代做回收。相反,在任一時刻隻有一部分老年代分區會被回收,並且,這部分老年代分區將與一次年輕代收集一起被收集。

貼士
術語混合(mixed)垃圾收集就是用來描述這種一部分老年代分區與年輕代收集結合在一起進行的收集。因此,混合GC就是在一次垃圾收集事件中,所有年輕代分區以及一部分老年代分區將會被回收。換句話說,混合GC就是將要被回收的年輕代與年老代分區的組合。

  與CMS GC類似,當遇到一些極端情況時,諸如老年代空間被消耗完了,會有一個安全措施來收集和壓縮整個老年代。

  撇開安全模式下的收集不談,一個G1老年代收集是由一係列階段組成,某些是並行stop-the-world的,某些是並行並發的。也就是說,某些階段是多線程的同時會暫停所有應用線程,而其他階段是多線程的,但可以與應用線程同時運行。

  當超過Java堆的占用閾值,G1就會啟動一次老年代收集。要注意到有一點非常重要,那就是G1中的堆占用閾值,這是根據老年代占用空間與整個Java堆空間相比較得出的。熟悉CMS GC的讀者會記得,CMS觸發老年代收集所用的占用閾值隻是相對於老年代空間本身而言的。在G1中,一旦達到或超過內存堆的占用閾值,一次並發stop-the-world方式的初始標記階段就會被安排執行。

  初始標記階段會跟著下一次年輕代收集同時進行。一旦初始標記階段結束,就會觸發一個並發多線程的標記階段,標記老年代中所有的存活對象。當並發標記階段結束,並行stop-the-world的重新標記階段就被啟動,標記那些因為在標記階段同時執行的應用線程導致產生錯過的對象。到重新標記階段結束,G1就擁有了老年代分區的完整信息。如果碰巧老年代分區裏一個存活對象都沒有,那麼在下一個階段——清除階段,不用做額外的垃圾收集工作就可以被回收再利用。

  同樣也是在重新標記階段結束,G1能識別出最適合回收的老年代分區集合。

貼士
在垃圾收集過程中收集的分區集合可以稱為收集集合(CSet)。

  選擇哪些分區被包含在一個CSet裏,是基於有多少空間可以被釋放以及G1暫停時間目標。在完成CSet識別之後,G1就在接下來的幾次年輕代垃圾收集過程中對CSet中的分區進行回收。也就是說,在接下來的幾個年輕代垃圾收集中,除了年輕代分區,還有一部分的老年代分區也將被回收。這就是前麵提到的垃圾收集事件中的混合GC類型。

  不管是年輕代還是老年代,G1會把每個收集過垃圾的分區中的存活對象轉移到一個可用分區中。一旦存活對象被轉移掉,那這個年輕代分區(可能還有老年代分區)就會被回收為可用分區。

  將各老年代分區中的存活對象轉移到可用分區會帶來一個很有吸引力的結果——在虛擬地址空間裏每個轉移對象都是前後相連的,對象和對象之間沒有碎片化的空餘空間。我們回憶一下,CMS、並行以及串行垃圾收集器都需要一個full GC來壓縮老年代,而這個壓縮動作需要掃描整個老年代空間。

  因為G1以每個分區為基礎做垃圾收集操作,所以它適用於大尺寸的Java堆。垃圾收集工作的數量可以被限製在一個小範圍的分區集合內,哪怕Java堆尺寸可能會相當大。

  G1暫停時間的最大來源是年輕代收集和混合收集,所以G1的設計目標之一就是允許用戶設置GC暫停時間目標。G1會嚐試通過調整Java堆尺寸大小的方式來滿足設定的暫停時間目標。它會根據暫停時間目標自動調整年輕代的尺寸和總的Java堆尺寸。暫停時間目標越短,年輕代空間就越小,總的堆空間就越大,使得老年代空間相對就越大。

  G1的設計目標就是把必要的調整限定在設置最大的Java堆空間和指定GC暫停時間目標上。另外,G1還被設計成可以通過一個內部的啟發式算法來做自我調整。在寫這本書的時候,G1的啟發式算法就是HotSpot GC開發活動開展的最活躍的地方。同樣到寫這本書時為止,G1也許會要求一些特定場景下的額外調優,不過開發優秀的啟發式算法的先決條件已經具備並且前景很可觀。至於如何對G1進行調優的建議。

  綜上,對於大的Java堆來說,通過將Java堆拆分成一個個分區,G1會比其他垃圾收集器有更好的綜合表現。在局部壓縮的幫助下,G1解決了Java堆碎片,它的絕大部分工作都通過多線程的方式完成。

  截止到寫這本文時,G1首要針對的是那些有合理短暫停的大尺寸Java堆的用例,當然還有那些正在使用CMS垃圾收集器的應用。也有計劃用G1來處理吞吐量用例,但對於那些追求高吞吐量,同時能容忍更長GC暫停的應用來說,目前並行垃圾收集器是更好的選擇。

  本文選自《Java性能調優指南》,點此鏈接可在博文視點官網查看此書。
                    圖片描述
  想及時獲得更多精彩文章,可在微信中搜索“博文視點”或者掃描下方二維碼並關注。
                       圖片描述

最後更新:2017-04-01 16:41:01

  上一篇:go 阿裏雲生態發力,聯手數千軟件商推新實體經濟崛起
  下一篇:go 川廚楊子第一次用雲