1. 程式人生 > >深入理解JVM(③)ZGC收集器

深入理解JVM(③)ZGC收集器

### 前言 **ZGC**是一款在JDK11中新加入的具有實驗性質的低延遲垃圾收集器,目前僅支援Linux/x86-64。ZGC收集器是一款基於Region記憶體佈局的,(暫時)不設分代的,使用了讀屏障、染色指標和記憶體多重對映等技術來實現可併發的標記-整理演算法的,以低延遲為首要目標的一款垃圾收集器。 ### ZGC佈局 與Shenandoah和G1一樣,ZGC也採取基於Region的堆記憶體佈局,但與他們不同的是,ZGC的Region具有動態性(動態的建立和銷燬,以及動態的區域容量大小)。 ZGC的Region可以分為三類: * **小型Region**:==容量固定為2MB,用於放置小於256KB的小物件。== * **中型Region**:==容量固定為32MB,用於放置大於等於256KB但小於4MB的物件。== * **大型Region**:==容量不固定,可以動態變化,但必須為2MB的整數倍,用於存放4MB或以上的大物件。並且每個大型Region只會存放一個物件。== ZGC記憶體佈局圖: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200620112717942.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_SmlNb2Vy,size_60,color_c8cae6,t_70) ### 染色指標 HotSpot的垃圾收集器,有幾種不同的標記實現方案。 * ==把標記直接記錄在物件頭上(Serial 收集器)==。 * ==把標記記錄在於物件相互獨立的資料結構上(G1、Shenandoah使用了一種相當於堆記憶體的1/64大小的,稱為BitMap的結構來記錄標記資訊)==。 * ==ZGC染色指標直接把標記資訊記載引用物件的指標上==。 染色指標是一種直接將少量額外的資訊儲存在指標上的技術。 目前Linux下64位指標的高18位不能用來定址,但剩餘的46位指標所能支援的64TB記憶體仍然鞥呢夠充分滿足大型伺服器的需要。鑑於此,ZGC將其高4位提取出來儲存四個標誌資訊。 ==通過這些標誌虛擬機器就可以直接從指標中看到器引用物件的三色標記狀態(Marked0、Marked1)、是否進入了重分配集(是否被移動過——Remapped)、是否只能通過finalize()方法才能被訪問到(Finalizable)==。由於這些標誌位進一步壓縮了原本只有46位的地址空寂,導致ZGC能夠管理的記憶體不可以超過4TB。 染色指標示意圖: ![染色指標示意圖](https://img-blog.csdnimg.cn/20200620144003255.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_Smltb2Vy,size_60,color_c8cae6,t_70) ### 染色指標的優勢 * 染色指標可以使得一旦某個Region的存活物件被移走之後,這個Region立即就能夠被釋放和重用掉,而不必等待整個堆中所有指令向該Region的引用都被修正後才能清理。 * 染色指標可以大幅減少在垃圾收集過程中記憶體屏障的使用數量,設定記憶體屏障,尤其是在寫屏障的目的通常是為了記錄物件引用的變動情況,如果將這些資訊直接維護在指標中,顯然就可以省去一些專門的記錄操作。 * 染色指標可以作為一種可擴充套件的儲存結構用來記錄更多與物件標記、重定位過程相關的資料,以便日後進一步提高效能。 ### 記憶體多重對映 Linux/x86-64平臺上ZGC使用了多重對映(Multi-Mapping)將多個不同的虛擬記憶體地址對映到同一實體記憶體地址上,這是一種多對一對映,意味著ZGC在虛擬記憶體中看到的地址空寂要比實際的堆記憶體容量來的更大。把染色指標中的標誌位看作是地址的分段符,那隻要將這些不同的地址段都對映到同一物理內褲空間,經過多重對映轉換後,就可以使用染色指標正常進行定址了。 多重對映下的定址: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200620155031205.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_Smltb2Vy,size_16,color_c8cae6,t_70) ### ZGC的運作過程 **ZGC**的運作過程大致可劃分為以下四個大的階段。四個階段都是可以併發執行的,僅是兩個階段中間會存在短暫的停頓小階段。 運作過程如下: ![ZGC執行過程](https://img-blog.csdnimg.cn/20200620161451497.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_Smltb2Vy,size_60,color_c8cae6,t_70) * **併發標記(Concurrent Mark):** 與G1、Shenandoah一樣,併發標記是遍歷物件圖做可達性分析的階段,前後也要經過類似於G1、Shenandoah的初始標記、最終標記的短暫停頓,而且這些停頓階段所做的事情在目標上也是相類似的。 * **併發預備重分配( Concurrent Prepare for Relocate):** 這個階段需要根據特定的查詢條件統計得出本次收集過程要清理哪些Region,將這些Region組成重分配集(Relocation Set)。 * **併發重分配(Concurrent Relocate):** 重分配是ZGC執行過程中的核心階段,這個過程要把重分配集中的存活物件複製到新的Region上,併為重分配集中的每個Region維護一個轉發表(Forward Table),記錄從舊物件到新物件的轉向關係。 * **併發重對映(Concurrent Remap):** 重對映所做的就是修正整個堆中指向重分配集中舊物件的所有引用,ZGC的併發對映並不是以一個必須要“迫切”去完成的任務。ZGC很巧妙地把併發重對映階段要做的工作,合併到下一次垃圾收集迴圈中的併發標記階段裡去完成,反正他們都是要遍歷所有物件的,這樣合併節省了一次遍歷的開銷。 ### ZGC的優劣勢 * 缺點:**浮動垃圾** 當ZGC準備要對一個很大的堆做一次完整的併發收集,駕駛其全過程要持續十分鐘以上,由於應用的物件分配速率很高,將創造大量的新物件,這些新物件很難進入當次收集的標記範圍,通常就只能全部作為存活物件來看待(儘管其中絕大部分物件都是朝生夕滅),這就產生了大量的浮動垃圾。 目前唯一的辦法就是儘可能地去增加堆容量大小,獲取更多喘息的時間。但若要從根本上解決,還是需要引入分代收集,讓新生物件都在一個專門的區域中建立,然後針對這個區域進行更頻繁、更快的收集。 * 優點:**高吞吐量、低延遲**。 ZGC是支援“NUMA-Aware”的記憶體分配。MUMA(Non-Uniform Memory Access,非統一記憶體訪問架構)是一種多處理器或多核處理器計算機所設計的記憶體架構。 ==現在多CPU插槽的伺服器都是Numa架構,比如兩顆CPU插槽(24核),64G記憶體的伺服器,那其中一顆CPU上的12個核,訪問從屬於它的32G本地記憶體,要比訪問另外32G遠端記憶體要快得多。 ZGC預設支援NUMA架構,在建立物件時,根據當前執行緒在哪個CPU執行,優先在靠近這個CPU的記憶體進行分配,這樣可以顯著的提高效能,在SPEC JBB 2005 基準測試裡獲得40%的提