Serial收集器
HotSpot虛擬機器執行在客戶端模式下的預設新生代收集器。
型別:單執行緒序列垃圾回收器
垃圾收集演算法:複製演算法
作用區域:新生代
特點:
1、只會用單個執行緒去完成垃圾收集工作,使用者執行緒會STW,直到收集結束。
2、沒有執行緒互動,專心做垃圾收集,獲得最高的單執行緒收集效率。
ParNew收集器
型別:多執行緒並行垃圾回收器
垃圾收集演算法:複製演算法
作用區域:新生代
本質上就是Serial收集器的並行版本。
特點:
1、能與CMS收集器搭配使用。
Parallel Scavenge收集器
型別:多執行緒並行垃圾回收器
垃圾收集演算法:複製演算法
作用區域:新生代
特點:
1、是以吞吐量優先的垃圾回收器。(吞吐量=使用者執行程式碼時間/使用者執行程式碼時間+垃圾收集時間)
2、不能與CMS收集器搭配使用。
3、內建有一個 PS MarkSweep收集器,但是實現原理跟Serial實現基本一樣。
為什麼CMS只能和ParNew搭配使用,而不能和Parallel Scavenge搭配使用?
就效能來看,Parallel Scavenge它的效能會比ParNew的效能要好一些。
但是CMS只能和ParNew搭配使用,原因如下:
1、CMS的設計目標是低延遲,Parallel Scavenge的設計目標是高吞吐量。
2、Parallel Scavenge沒有使用HotSpot的分代框架,而CMS使用了HotSpot的分代框架。(ParNew使用了HotSpot的分代框架)
Serial Old收集器
型別:單執行緒串型收集器
垃圾收集演算法:標記-整理演算法
作用區域:老年代
Parallel Old收集器
型別:多執行緒並行收集器
垃圾收集演算法:標記-整理演算法
作用區域:老年代
特點:
1、緩解了Parallel Scanvenge的尷尬局面。(在其出之前Parallel Scanvenge只能和Serial搭配使用)
CMS收集器
這款收集器是HotSpot虛擬機器中第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集執行緒與使用者執行緒同時工作。
CMS收集器的關注點是儘可能縮短垃圾收集時使用者執行緒的停頓時間。停頓時間越短(低延遲)就越適合與使用者互動的程式,良好的響應速度能提升使用者體驗。
型別:多執行緒併發收集器
垃圾收集演算法:標記-清除演算法
作用區域:老年代
工作原理
-
初始標記階段:所有工作執行緒都會因為"Stop-the-World"機制而短暫暫停,這個階段主要任務僅僅只是標記出GC Roots能直接關聯到的物件。
-
併發標記階段:從GC Roots的直接關聯物件開始遍歷整個物件圖的過程,這個過程耗時較長但是不需要使用者執行緒停頓。掃描完成後,這個過程可能會造成物件引用的改變。CMS使用了增量標記的技術,把改變的都引用發起者(黑色物件)儲存起來。
-
重新標記階段:主要是處理併發階段中儲存起來的黑色物件,在STW的前提下,重新以他們為根進行標記。
-
併發清除階段:這個階段清除掉標記階段判斷已經死亡的物件,釋放記憶體空間。這個階段可以與使用者執行緒併發。
為什麼採用標記-清除演算法?
因為在清除過程中,GC執行緒是跟使用者併發的,如果使用複製或標記-整理演算法會造成物件記憶體地址的改變,那麼會造成使用者持有原先的引用而無法訪問到物件的情況。
特點
1、觸發CMS垃圾回收器時需要預留一定的空間來支援使用者建立物件。
觸發的闕值我們可以設定,但是在極端情況下,如果設定闕值過高,造成無法滿足程式分配物件,那麼虛擬機器就會被迫啟動Serial Old收集器來進行收集,這就會造成長時間的STW了。
2、會產生浮動垃圾
3、進行Full GC時,會進行記憶體碎片的整理
4、CMS對CPU資源非常敏感,在併發階段時,雖然不會造成使用者執行緒的STW,但是會因為佔有CPU資源,而導致應用程式變慢,降低總吞吐量。
G1垃圾收集器
具有跨時代意義的垃圾收集器,設計思想:區域性收集、基於Region的記憶體佈局形式。
設計目的:回收垃圾最大化。
作用區域:整個堆
垃圾收集演算法:從整體上來看是標記-整理演算法。但從兩個Region的角度來看,是標記-複製演算法。
Region
G1保留了年輕代和老年代的概念,但新生代和老年代不再是固定的了,而是一系列不需要連續的動態集合。
其中,Homongous區域主要用來存放大物件。G1認為只要大小超過了一半的Region區域的物件就稱為大物件。大多數情況下,G1把Homongous當成老年代看待。
設定H區域的原因
之前遇到大物件且年輕代Eden空間不夠的話,那麼會將大物件放到老年代,那麼這個大物件得等到Old GC或者Full GC才能得以清理。
而設定H區之後:1、避免了記憶體碎片 2、提高了回收大物件的效率
工作原理
-
初始標記階段:所有工作執行緒都會因為"Stop-the-World"機制而短暫暫停,這個階段主要任務僅僅只是標記出GC Roots能直接關聯到的物件。
-
併發標記階段:從GC Root開始對堆中物件進行可達性分析,這個過程與使用者執行緒併發執行。掃描完成後,這個過程中可能會存在物件引用的改變,G1使用原始快照(STAB)方式來避免出現漏標的情況。
-
重新標記階段:主要處理併發標記過程中的STAB記錄,在STW的前提下,以他們為根進行重新標記。
-
清除階段:這個階段需要使用者執行緒STW。這個階段清除掉標記階段判斷已經死亡的物件,釋放記憶體空間。(使用的是複製演算法)
問題
跨Region的引用問題:
當最開始我們使用分代模型的時候,只需要注意跨代引用,我們解決的思路是透過記憶集統計老年代到年輕代的引用即可。
而跨Region,我們就需要記錄得跟複雜了。解題思路還是透過記憶集來記錄跨Region引用,透過空間換時間的思路來避免全堆作為GC Root掃描。但是這個記憶集就需要每個Region都去維護自己獨特的一個了,每個Region的記憶集不僅要記錄我引用了誰,也要記錄誰引用了我。
所以,記憶集造成了比較大的空間浪費。(10%~20%的堆空間浪費)
併發標記階段如何避免漏標問題:
漏標:把存活的物件給他誤刪了。
G1使用了原始快照來解決漏標問題。當灰色物件要解開和白色物件的引用時,把白色物件記錄下來。並在後面以他們為根,去掃描,讓他們存活。(思路:就是以可能存在浮動垃圾的下,減少掃描的時間。)
Young GC & Mixed GC
G1透過構造一個可預測的時間模型,來在特定的時間內收穫最高的垃圾。
那麼在收集時,就不會只侷限於年輕代了,當涉及多個代時,我們稱為Mixed GC。
當只涉及年輕代時,我們還是稱為Young GC。
ZGC垃圾收集器
概述
ZGC是JDK11推出的低延遲垃圾回收器。
設計目標:
1、停頓時間不超過10ms(低停頓)
2、停頓時間不會隨著堆大小或者活躍物件的大小而增加。
適用場景:大記憶體低延遲服務。
前者GC的痛點
前者的GC在STW這塊都不是特別友善,所以在一些低延遲服務造成了效能困擾。
CMS(ParNew)與G1停頓時間瓶頸
ParNew和G1使用的都是標記-複製演算法。
瓶頸定位→複製階段中的轉移階段中複製物件。
複製階段中轉移階段是STW的,轉移階段需要分配新記憶體和複製物件的成員變數。其中記憶體分配比較快,但是在複製一些複雜物件的時候耗時會比較長。
為什麼轉移階段不能和使用者併發執行呢?
主要是無法解決轉移過程中精確定位物件地址的問題。如果併發執行,那麼會導致使用者訪問不到具體物件。
ZGC的原理
全併發
ZGC採用標記-複製演算法。不過ZGC在標記、轉移和重定位階段幾乎都是併發的。
ZGC只有三個STW階段:初始標記,再標記,初始轉移。其中,初始標記和初始轉移分別都只需要掃描所有GC Roots,其處理時間和GC Roots的數量成正比,一般情況耗時非常短;再標記階段STW時間很短,最多1ms,超過1ms則再次進入併發標記階段。即,ZGC幾乎所有暫停都只依賴於GC Roots集合大小,停頓時間不會隨著堆的大小或者活躍物件的大小而增加。與ZGC對比,G1的轉移階段完全STW的,且停頓時間隨存活物件的大小增加而增加。
ZGC的技術
著色指標
著色指標是一種將資訊儲存在指標中的技術。
當應用程式建立物件時,首先在堆空間申請一個虛擬地址。同時會在M0、M1、Remapped地址空間分別申請一個虛擬地址,且三個虛擬地址對應同一個實體地址,但這三個虛擬地址在同一時間只有一個空間有效。
讀屏障
Object o = obj.FieldA // 從堆中讀取引用,需要加入屏障
<Load barrier>
Object p = o // 無需加入屏障,因為不是從堆中讀取引用
o.dosomething() // 無需加入屏障,因為不是從堆中讀取引用
int i = obj.FieldB //無需加入屏障,因為不是物件引用
ZGC中讀屏障的程式碼作用:在物件標記和轉移過程中,用於確定物件的引用是否滿足條件,並做出對應的動作。
ZGC的併發處理過程
1、初始化:ZGC初始化之後,整個記憶體空間的地址檢視被設定成Remapped。程式正常執行,在記憶體中分配物件,滿足一定條件後垃圾回收啟動,進入標記階段。
2、併發標記階段:第一次進入標記階段時檢視為M0,如果物件被GC標記執行緒或者應用執行緒訪問過,那麼就講物件的地址檢視從Remapped調整為M0。所以,在標記階段之後,如果在M0就說明物件是活躍的,如果是在Remapped就說明物件是不活躍的。
3、併發轉移階段:標記結束後就進入轉移階段,此時地址檢視再次被設定為Remapped,如果物件被GC標記執行緒或者應用程式訪問過,那麼會從M0調整為Remapped。
其實,在標記階段存在兩個地址檢視M0和M1,上面的過程顯示只用了一個地址檢視。之所以設計成兩個,是為了區別前一次標記和當前標記。也即,第二次進入併發標記階段後,地址檢視調整為M1,而非M0。
著色指標和讀屏障技術不僅應用在併發轉移階段,還應用在併發標記階段:將物件設定為已標記,傳統的垃圾回收器需要進行一次記憶體訪問,並將物件存活資訊放在物件頭中;而在ZGC中,只需要設定指標地址的第42~45位即可,並且因為是暫存器訪問,所以速度比訪問記憶體更快。