1. 程式人生 > >JDK11 | 第七篇 : ZGC 垃圾收集器

JDK11 | 第七篇 : ZGC 垃圾收集器

一、簡介

Java 11包含一個全新的垃圾收集器--ZGC,它由Oracle開發,承諾在數TB的堆上具有非常低的暫停時間。 在本文中,我們將介紹開發新GC的動機,技術概述以及由ZGC開啟的一些可能性。

那麼為什麼需要新GC呢?畢竟Java 10已經有四種釋出多年的垃圾收集器,並且幾乎都是無限可調的。 換個角度看,G1是2006年時引入Hotspot VM的。當時最大的AWS例項有1 vCPU和1.7GB記憶體,而今天AWS很樂意租給你一個x1e.32xlarge例項,該型別例項有128個vCPU和3,904GB記憶體。 ZGC的設計目標是:支援TB級記憶體容量,暫停時間低(<10ms),對整個程式吞吐量的影響小於15%。 將來還可以擴充套件實現機制,以支援不少令人興奮的功能,例如多層堆(即熱物件置於DRAM和冷物件置於NVMe快閃記憶體),或壓縮堆。

二、GC術語

為了理解ZGC如何匹配現有收集器,以及如何實現新GC,我們需要先了解一些術語。最基本的垃圾收集涉及識別不再使用的記憶體並使其可重用。現代收集器在幾個階段進行這一過程,對於這些階段我們往往有如下描述:

  • 並行:在JVM執行時,同時存在應用程式執行緒和垃圾收集器執行緒。 並行階段是由多個gc執行緒執行,即gc工作在它們之間分配。 不涉及GC執行緒是否需要暫停應用程式執行緒。

  • 序列:序列階段僅在單個gc執行緒上執行。與之前一樣,它也沒有說明GC執行緒是否需要暫停應用程式執行緒。

  • STW:STW階段,應用程式執行緒被暫停,以便gc執行其工作。 當應用程式因為GC暫停時,這通常是由於Stop The World階段。

  • 併發:如果一個階段是併發的,那麼GC執行緒可以和應用程式執行緒同時進行。 併發階段很複雜,因為它們需要在階段完成之前處理可能使工作無效。

  • 增量:如果一個階段是增量的,那麼它可以執行一段時間之後由於某些條件提前終止,例如需要執行更高優先順序的gc階段,同時仍然完成生產性工作。 增量階段與需要完全完成的階段形成鮮明對比。

三、工作原理

現在我們瞭解了不同gc階段的屬性,讓我們繼續探討ZGC的工作原理。 為了實現其目標,ZGC給Hotspot Garbage Collectors增加了兩種新技術:著色指標和讀屏障。

著色指標

著色指標是一種將資訊儲存在指標(或使用Java術語引用)中的技術。因為在64位平臺上(ZGC僅支援64位平臺),指標可以處理更多的記憶體,因此可以使用一些位來儲存狀態。 ZGC將限制最大支援4Tb堆(42-bits),那麼會剩下22位可用,它目前使用了4位: finalizable, remap, mark0和mark1。 我們稍後解釋它們的用途。

著色指標的一個問題是,當您需要取消著色時,它需要額外的工作(因為需要遮蔽資訊位)。 像SPARC這樣的平臺有內建硬體支援指標遮蔽所以不是問題,而對於x86平臺來說,ZGC團隊使用了簡潔的多重對映技巧。

多重對映

要了解多重對映的工作原理,我們需要簡要解釋虛擬記憶體和實體記憶體之間的區別。 實體記憶體是系統可用的實際記憶體,通常是安裝的DRAM晶片的容量。 虛擬記憶體是抽象的,這意味著應用程式對(通常是隔離的)實體記憶體有自己的檢視。 作業系統負責維護虛擬記憶體和實體記憶體範圍之間的對映,它通過使用頁表和處理器的記憶體管理單元(MMU)和轉換查詢緩衝器(TLB)來實現這一點,後者轉換應用程式請求的地址。

多重對映涉及將不同範圍的虛擬記憶體對映到同一實體記憶體。 由於設計中只有一個remap,mark0和mark1在任何時間點都可以為1,因此可以使用三個對映來完成此操作。 ZGC原始碼中有一個很好的圖表可以說明這一點。

讀屏障

讀屏障是每當應用程式執行緒從堆載入引用時執行的程式碼片段(即訪問物件上的非原生欄位non-primitive field):

void printName( Person person ) {
    String name = person.name;  // 這裡觸發讀屏障
                                // 因為需要從heap讀取引用 
                                // 
    System.out.println(name);   // 這裡沒有直接觸發讀屏障
}

在上面的程式碼中,String name = person.name 訪問了堆上的person引用,然後將引用載入到本地的name變數。此時觸發讀屏障。 Systemt.out那行不會直接觸發讀屏障,因為沒有來自堆的引用載入(name是區域性變數,因此沒有從堆載入引用)。 但是System和out,或者println內部可能會觸發其他讀屏障。

這與其他GC使用的寫屏障形成對比,例如G1。讀屏障的工作是檢查引用的狀態,並在將引用(或者甚至是不同的引用)返回給應用程式之前執行一些工作。 在ZGC中,它通過測試載入的引用來執行此任務,以檢視是否設定了某些位。 如果通過了測試,則不執行任何其他工作,如果失敗,則在將引用返回給應用程式之前執行某些特定於階段的任務。

標記

現在我們瞭解了這兩種新技術是什麼,讓我們來看看ZG的GC迴圈。

GC迴圈的第一部分是標記。標記包括查詢和標記執行中的應用程式可以訪問的所有堆物件,換句話說,查詢不是垃圾的物件。

ZGC的標記分為三個階段。 第一階段是STW,其中GC roots被標記為活物件。 GC roots類似於區域性變數,通過它可以訪問堆上其他物件。 如果一個物件不能通過遍歷從roots開始的物件圖來訪問,那麼應用程式也就無法訪問它,則該物件被認為是垃圾。從roots訪問的物件集合稱為Live集。GC roots標記步驟非常短,因為roots的總數通常比較小。

該階段完成後,應用程式恢復執行,ZGC開始下一階段,該階段同時遍歷物件圖並標記所有可訪問的物件。 在此階段期間,讀屏障針使用掩碼測試所有已載入的引用,該掩碼確定它們是否已標記或尚未標記,如果尚未標記引用,則將其新增到佇列以進行標記。

在遍歷完成之後,有一個最終的,時間很短的的Stop The World階段,這個階段處理一些邊緣情況(我們現在將它忽略),該階段完成之後標記階段就完成了。

重定位

GC迴圈的下一個主要部分是重定位。重定位涉及移動活動物件以釋放部分堆記憶體。 為什麼要移動物件而不是填補空隙? 有些GC實際是這樣做的,但是它導致了一個不幸的後果,即分配記憶體變得更加昂貴,因為當需要分配記憶體時,記憶體分配器需要找到可以放置物件的空閒空間。 相比之下,如果可以釋放大塊記憶體,那麼分配記憶體就很簡單,只需要將指標遞增新物件所需的記憶體大小即可。

ZGC將堆分成許多頁面,在此階段開始時,它同時選擇一組需要重定位活動物件的頁面。選擇重定位集後,會出現一個Stop The World暫停,其中ZGC重定位該集合中root物件,並將他們的引用對映到新位置。與之前的Stop The World步驟一樣,此處涉及的暫停時間僅取決於root的數量以及重定位集的大小與物件的總活動集的比率,這通常相當小。所以不像很多收集器那樣,暫停時間隨堆增加而增加。

移動root後,下一階段是併發重定位。 在此階段,GC執行緒遍歷重定位集並重新定位其包含的頁中所有物件。 如果應用程式執行緒試圖在GC重新定位物件之前載入它們,那麼應用程式執行緒也可以重定位該物件,這可以通過讀屏障(在從堆載入引用時觸發)實現,如流程圖如下所示:

這可確保應用程式看到的所有引用都已更新,並且應用程式不可能同時對重定位的物件進行操作。

GC執行緒最終將對重定位集中的所有物件重定位,然而可能仍有引用指向這些物件的舊位置。 GC可以遍歷物件圖並重新對映這些引用到新位置,但是這一步代價很高昂。 因此這一步與下一個標記階段合併在一起。在下一個GC週期的標記階段遍歷物件物件圖的時候,如果發現未重對映的引用,則將其重新對映,然後標記為活動狀態。

概括

試圖單獨理解複雜垃圾收集器(如ZGC)的效能特徵是很困難的,但從前面的部分可以清楚地看出,我們所碰到的幾乎所有暫停都只依賴於GC roots集合大小,而不是實時堆大小。標記階段中處理標記終止的最後一次暫停是唯一的例外,但是它是增量的,如果超過gc時間預算,那麼GC將恢復到併發標記,直到再次嘗試。

三、效能

那ZGC到底表現如何?

Stefan Karlsson和Per Liden在今年早些時候的Jfokus演講中給出了一些數字。 ZGC的SPECjbb 2015吞吐量與Parallel GC(優化吞吐量)大致相當,但平均暫停時間為1ms,最長為4ms。 與之相比G1和Parallel有很多次超過200ms的GC停頓。

然而,垃圾收集器是複雜的軟體,從基準測試結果可能無法推測出真實世界的效能。我們期待自己測試ZGC,以瞭解它的效能如何因工作負載而異。

本文參考:https://mp.weixin.qq.com/s/nAjPKSj6rqB_eaqWtoJsgw




歡迎掃碼或微信搜尋公眾號《程式設計師果果》關注我,關注有驚喜~

相關推薦

JDK11 | : ZGC 垃圾收集

一、簡介 Java 11包含一個全新的垃圾收集器--ZGC,它由Oracle開發,承諾在數TB的堆上具有非常低的暫停時間。 在本文中,我們將介紹開發新GC的動機,技術概述以及由ZGC開啟的一些可能性。 那麼為什麼需要新GC呢?畢竟Java 10已經有四種釋出多年的垃圾收集器,並且幾乎都是無限可調的。 換個角度

讀書筆記 ---- 《深入理解Java虛擬機器》---- 3垃圾收集

上一篇:垃圾回收演算法:https://blog.csdn.net/pcwl1206/article/details/84061589 本篇文章轉發自:https://blog.csdn.net/chjttony/article/details/7883748 第3篇:垃圾收集器 1&n

深入理解JVM—三章:垃圾收集與記憶體分配策略

概述 對於Java記憶體執行時區域的各位部分,其中程式計數器、虛擬機器棧、本地方法棧這三個區域都是隨執行緒而生,隨執行緒而滅。並且棧幀中分配的記憶體也是在編譯後就已知的。因此這幾個區域的記憶體分配和回收都具備確定性,所以我們在這幾個區域就不必過多地考慮

五章 JVM垃圾收集(1)

說明:垃圾回收演算法是理論,垃圾收集器是回收演算法的實現,關於回收演算法,見《第四章 JVM垃圾回收演算法》 1、七種垃圾收集器 Serial(序列GC)-- 複製 ParNew(並行GC)-- 複製 Parallel Scavenge(並行回收GC)-- 複製 Serial Old(MSC)(

六章 JVM垃圾收集(2)

1、G1 說明: 從上圖來看,G1與CMS相比,僅在最後的"篩選回收"部分不同(CMS是併發清除),實際上G1回收器的整個堆記憶體的劃分都與其他收集器不同。 CMS需要配合ParNew,G1可單獨回收整個空間 原理: G1收集器將整個堆劃分為多個大小相等的Region G1

JVM 第二垃圾收集以及演算法

![](https://cdn.geekdigging.com/java/java_header.jpg) > 本文內容過於硬核,建議有 Java 相關經驗人士閱讀。 ## 0. 引言 一說到 JVM ,大多數人第一個想到的可能就是 GC ,今天我們就來聊一聊和 GC 關係最大的垃圾收集器以及垃圾收集演

css選擇實現字段解析

resp 文章 elf span ext div ant rec normalize CSS選擇器的作用實際和xpath的一樣,都是為了定位具體的元素 舉例我要爬取下面這個頁面的標題 In [20]: title = response.css(".ent

垃圾收集與內存分配策略之二:垃圾收集

開啟 full gc 行處理 意義 方案 發現 特征 sea 互聯網 五、垃圾收集器 如果說收集算法是內存回收的方法論,那麽垃圾收集器就是內存回收的具體實現。由於java虛擬機規範對垃圾收集器實現沒有任何的規範因此不同的廠商,不同的版本的虛擬機所提供的垃圾收集器都有可

《深入理解Java虛擬機》學習筆記(三章 垃圾收集與內存分配策略)

關鍵字 rem 永久 規模 是把 同時 技術 source () 第三章 垃圾收集器與內存分配策略 要解決的問題 哪些內存需要回收? 什麽時候回收? 如何回收? 概述 當需要排查各種內存溢出、內存泄漏問題時,當垃圾收集成為系統達到更高並發量的瓶頸時, 需要對內存動態分

三章垃圾收集與記憶體分配策略

3.2物件死亡的判斷方法 3.2.1引用計數法 給物件新增一個引用計數器,每當一個地方引用它就+1,引用失效就-1,當計數器為0時就表示物件已經死亡。 缺點是無法解決迴圈引用問題 3.2.2可達性分析 將GC root作為根節點向下遍歷,無法遍歷到的物件(GC Root到這個物件不可達)就表示該物件

三章垃圾收集與內存分配策略

永久代 調用 標記清理 參數 通過 大小 整合 虛擬機 分析 3.2對象死亡的判斷方法 3.2.1引用計數法 給對象添加一個引用計數器,每當一個地方引用它就+1,引用失效就-1,當計數器為0時就表示對象已經死亡。 缺點是無法解決循環引用問題 3.2.2可達性分析 將GC

《深入理解JAVA虛擬機器》詳細解讀(三章 ):垃圾收集與記憶體分配策略

  目錄 一、垃圾收集器與記憶體分配策略 1 概述 2 物件已經死亡? 2.1引用計數法(未使用) 2.2可達性分析演算法 2.3 再談引用 2.4 生存還是死亡 2.5 回收方法區 3 垃圾收集演算法 3.1 複製演算法(Copy) 3

【深入理解JVM虛擬機器】3章 垃圾收集與記憶體分配策略

3.1 概述 垃圾收集(Garbage Collection , GC)的歷史遠遠比Java久遠。它需要完成三件事: 哪些記憶體需要回收 什麼時候回收 如何回收 程式計數器、虛擬機器棧、本地

三章 垃圾收集與記憶體分配策略

3.1 概述       1960年誕生的Lisp是第一門真正使用記憶體動態分配和垃圾收集技術的語言。程式計數器、虛擬機器棧、本地方法棧三個區域隨執行緒而生,隨執行緒而滅;棧中的棧幀隨著方法的進入和退出而有條不紊的執行著出棧和入棧操作。每一個棧幀中分配多少記憶體基本上是在類

JVM 3章垃圾收集與記憶體分配策略

1 概述 程式計數器、虛擬機器棧、本地方法棧這3個區域隨執行緒而生,隨執行緒而滅。每個棧幀中分配多少記憶體基本是在類結構確定下來時就已知的,因此這幾個區域的記憶體分配和回收都具備確定性。而java堆和方法區是執行緒共享的記憶體,且一個介面中的多個實現類需要的記憶體可能不一樣,一個方法中的

Spring Cloud系列教程 | :使用Spring Cloud Zuul實現過濾器或攔截功能案例

推薦 Spring Cloud 視訊: 使用Spring Cloud Zuul實現過濾器或攔截器功能案例   Spring Cloud的API閘道器不但可以實現類似NGINX+Lua強大的路由分發,實現動靜頁面的分流,更重要可以實現對所有發往後端微服務請求的攔

JDK11-G1垃圾收集

同時歡迎觀看本人錄得兩個視訊教程: G1垃圾收集器簡介 G1垃圾收集器主要是為那些擁有大記憶體的多核處理器而設計的。它在以很高的概率滿足垃圾收集的停頓時間的要求同時還可以達到很高的吞吐量,同時幾乎不需要做什麼配置。G1的目標是為應用提供停頓時間和吞吐量的最

Java8筆記(Stream API 的操作->規約,收集

深入 java8 第 07 篇 ( Stream API 的操作->規約,收集 ) 一、Stream API 的操作步驟: 建立 Stream 中間操作 終止操作(終端操作 ->

SpringMVC【RESTful支援、攔截

RESTful支援 我們在學習webservice的時候可能就聽過RESTful這麼一個名詞,當時候與SOAP進行對比的…那麼RESTful究竟是什麼東東呢??? RESTful(Representational State Transfer)軟體開發理念,

三章:垃圾回收-G1收集

類型 排序 world left 劃分 member 解決 soft 使用 G1是一款面向服務端的垃圾回收器,它是作用是替換到JDK1.5中發布的CMS收集器,與其他收集器相比,G1具有以下優點: 並行與並發 利用多核CPU來縮短Stop the wor