垃圾收集器與記憶體分配策略_hotspot垃圾收集演算法實現和垃圾收集器

z1340954953發表於2018-03-28

HotSpot演算法實現

1. 列舉根節點

從可達性分析中從GC Roots節點中找引用鏈這個操作為例,可作為GC Roots的節點主要在全域性性的引用(常量或類靜態屬性)與執行上下文(例如棧幀中的本地變數表)中。如果逐個檢查,消耗很多資源

另外,可達性分析對執行時間的敏感還體現在GC停頓上,因為這項分析工作必須在一個能保證一致性的快照中進行-這裡的一致性指的是在整個分析期間整個執行系統看起來就像是凍結在某個時間點上。如果一致性無法保證,分析結果就無法保證。

這就是GC進行時,必須停頓所有java執行執行緒的一個原因.即使在幾乎不會發生停頓的CMS收集器中,列舉根節點也是必須停頓的

那麼主流的虛擬機器是如何檢查完所有的GC Roots呢?

使用一組稱為OopMap的資料結構,在類載入完成,將物件內什麼偏移量上是什麼型別的資料計算出來,在JIT編譯過程中,也會在特定的位置記錄棧和暫存器中哪些位置是引用。

2. 安全點

HotSpot不會為每條指令都生成OopMap,只是在特定的位置記錄這些資訊,這些位置稱為安全點。程式執行時並非所有的地方都能停頓下來開始GC,只有到達安全點時才能暫停。

安全點的選定基本上是以“是否具有讓程式長時間執行的特性”為標準進行選定的,“長時間執行”的最明顯特徵就是指令序列複用,例如方法複用、迴圈跳轉、異常跳轉等,這些功能指令才會產生Safepoint

3. 安全區域

安全點機制保證了程式執行時,在不太長的時間內就會遇到可進入GC的Safepoint。但是程式不執行時候?

典型的例子就是執行緒處於Sleep狀態或者Blocked狀態,這時候執行緒無法響應JVM的中斷請求,到安全點去中斷掛起,

JVM也顯然不可能等待執行緒重新被分配CPU時間,這種情況需要安全區域解決。

安全區域指的是在一段程式碼時間內,引用關係不會發生變化。在這個區域中的任何地方開始GC都是安全的。

線上程執行到安全區域,首先標識自己已經進入了safe region,那樣如果JVM發起GC時候,不用管標識自己為Safe Region狀態的執行緒了。執行緒離開安全區域,檢查系統是否完成根節點列舉,完成,執行緒就會執行執行,否則等待直到可以離開安全區域的訊號

垃圾收集器

Serial收集器

一個單執行緒的垃圾收集器,單執行緒指的是進行垃圾收集時候,必須暫停其他所有的工作執行緒,直到收集結束


它是虛擬機器執行在Client模式下預設的新生代收集器。有著優於其他垃圾收集器的地方:簡單而高效,對於限定單個CPU的環境來說,Serial收集器沒有執行緒互動的開銷,專心做垃圾收集可以獲得最高的單執行緒收集效率

Serial Old收集器

Serial收集器的老年代版本,同樣是一個單執行緒的垃圾收集器,使用標記-整理演算法。

一種用途在JDK1.5以前版本和Parallel SCavenge收集器搭配使用

一種作為CMS收集器的後備方案

ParNew收集器

是Serial收集器的多執行緒版本,除了使用多條執行緒進行垃圾收集外,其餘行為包括Serial收集器可用的所有控制引數、收集演算法、Stop The World 、物件分配規則、回收策略等都與Serial收集器完全一樣。


作為許多執行在Server模式下的虛擬機器中首選的新生代垃圾收集器,其中一個與效能無關的重要原因是,除了Serial收集器外,目前只有它能喝CMS收集器配合工作。

從ParNew收集器開始,後面會接觸到幾款併發和並行的收集器。在垃圾收集器的語境中解釋如下:

並行(Parallel): 指多條垃圾收集執行緒並行工作,既然垃圾收集器在工作了,使用者執行緒必須是等待狀態的

併發(Concurrent): 指使用者執行緒與垃圾收集執行緒同時執行(但不一定是並行的,可能會交替執行),使用者程式在繼續執行,而垃圾收集器執行在另一個CPU上。

Parallel Scavenge收集器

是新生代的垃圾收集器,也是使用複製演算法的收集器,並且是並行的多執行緒收集器,看上去和ParNew都一樣,有什麼特殊?

Parrallel Scavenge收集器的特點是它的關注點和其他不同,CMS等垃圾收集的關注點是儘可能縮短垃圾收集時使用者執行緒的停頓時間,Parrralle Scavenge目標是達到一個可控制的吞吐量

Parallel Old收集器

作為Parallel SCAvenge收集器的老年代版本,使用多執行緒和標記-整理演算法搭配新生代垃圾收集器Parallel Scavenge使用


CMS收集器

CMS收集器是一種以獲取最短回收停頓時間為目標的收集器。CMS收集器是基於標記-清除演算法實現的,執行過程分為:

1> 初始標記

2> 併發標記

3> 重新標記

4> 併發清除

其中,

初始標記,重新標記這兩個步驟仍然需要Stop the world 。

初始標記僅僅只是標記一下GC Roots能直接關聯到的物件,速度很快。

併發標記階段就是進行GC Roots Tracing的過程

重新標記階段則是為了修正併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分物件的標記記錄。這個階段的停頓時間一般會比初始標記階段稍長,但是遠比並發標記時間短。

整個過程中耗時最長的併發標記和併發清除過程收集器執行緒都可以和使用者執行緒一起工作,所以,總體上看,CMS收集器的記憶體回收過程是與使用者執行緒一起併發執行的。

優點

併發收集、低停頓

缺點

對CPU資源非常敏感,當CPU不足4個,效能下降很多。

無法處理浮動垃圾(由於使用者執行緒是執行著,無法當次清理執行中產生的垃圾)

採用標記-清除演算法,產生的記憶體空間不連續的問題,無法放置大物件,會再次觸發一次垃圾收集

G1收集器

一款面向伺服器的垃圾收集器,G1具備如下特點:

1> 並行和併發

2> 分代收集。不需要其他收集器配合就能管理整個GC堆,按照新生代和老年代分開處理

3> 空間整合。和CMS不同,G1從整體上看是基於"標記-整理"演算法實現的收集器,從區域性(兩個Region之間)上看是基於複製演算法實現的,也就是G1執行期間不會產生記憶體空間碎片的,收集後能提供規整的可用記憶體

4> 可預測的停頓。

G1收集器,將java堆劃分為多個大小相等的獨立區域,雖然還保留新生代和老年代的概念,但是新生代和老年代不再是物理隔離了。

G1收集器的運作大致可劃分為以下幾個步驟:

1> 初始標記

2> 併發標記

3> 最終標記

4> 篩選回收

初始標記階段只是標記一下GC Roots能直接關聯到的物件,並且修改TAMS(Next Top at Mark Start)的值,讓下一階段使用者程式併發執行時,能在正確可用的Region中建立新物件,這階段需要停頓執行緒,但是耗時很短

併發標記階段是從GC Roots開始對堆中物件進行可達性分析,找出存活物件,耗時比較長

最終標記階段則是為了修正在併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分標記記錄,將這段時間物件變化記錄到執行緒Remembered Set Logs裡面,最終標記階段需要把Remembered Set logs的資料合併到 remembered Set中,需要停頓執行緒,但是可以並行執行。

篩選回收階段對麼個region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間制定回收計劃。

相關文章