當前位置: 首頁>>技術教程>>正文


ZGC簡介:可擴展的低延時JVM垃圾收集器

注:JAVA自動垃圾收集器是把雙刃劍,既帶來了無數開發便利,也引入了不少StopTheWorld這樣的性能問題。如何解決這些問題,且看ZGC!

1.簡介

今天,應用程序同時為數千甚至數百萬用戶提供服務的情況並不少見。這些應用程序通常需要大量內存。但不得不小心的是,內存管理非常容易影響應用程序性能。

為了解決這個問題,Java 11引入了Z垃圾收集器(ZGC)作為垃圾收集器(GC)實現(2019.05.15注:當前這個功能為實驗性)。

在本教程中,我們將看到ZGC如何設法在數TB的內存堆上保持低暫停時間

2.主要概念

為了理解ZGC,我們需要了解背後的基本概念和術語內存管理垃圾收集器

2.1。內存管理

物理內存是我們的硬件提供的RAM。

操作係統(OS)為每個應用分配虛擬內存空間。

當然,我們將虛擬內存存儲在物理內存中,操作係統負責維護兩者之間的映射。此映射通常涉及硬件加速。

2.2。垃圾收集

當我們創建Java應用程序時,我們不必釋放我們分配的內存,因為垃圾收集器會為我們執行此操作。綜上所述,GC可以通過一係列的內存引用識別判斷應用程序中的哪些對象可達,從而釋放不可達的對象

為實現這一目標,垃圾收集器有多個階段。

2.3。 GC相屬性

GC階段可以具有不同的屬性:

  • 並行階段:可以在多個GC線程上運行
  • 串行階段:單個線程上運行
  • Stop-the-world階段:GC線程不能與應用程序代碼同時運行
  • 並發階段:GC線程可以在後台運行,而應用程序可以完成它的工作
  • 漸進式階段:GC可以在完成所有工作之前終止,並在以後繼續

請注意,所有上述技術都有其優點和缺點。例如,假設我們有一個可以與我們的應用程序同時運行的階段。此階段的串行實現需要1%的整體CPU性能並運行1000ms。相比之下,並行實現使用30%的CPU並在50ms內完成其工作。

在這個例子中,並行解決方案使用更多的CPU資源,因為它可能更複雜並且必須同步線程。對於CPU密集型應用程序(例如批處理作業)來說,這是一個問題,因為一旦CPU計算能力較低時導致無法進行有效的工作。

當然,這個例子的數字可能不是真實的。但是,顯而易見所有應用程序都有自己的特性,因此它們有不同的GC要求。

有關更詳細的說明,請訪問我們關於Java內存管理的文章

3. ZGC Concepts

除了久經考驗的GC技術之外,ZGC還引入了兩個新概念:指針著色和Load barriers。

3.1。指針著色

指針表示虛擬內存中字節的位置。但是,我們不一定要使用指針的所有位來執行此操作 – 一些位可以表示指針的屬性。這就是我們所說的指針著色。

使用32位,我們可以處理4千兆字節。由於現在配置的內存比這個更常見,我們顯然不能使用這32位中的任何一個進行著色。因此,ZGC使用64位指針。這意味著ZGC僅適用於64位平台:

ZGC指針使用42位來表示地址本身。因此,ZGC指針可以處理4TB的內存空間。

最重要的是,我們有4位來存儲指針狀態:

  • 終結bit – 隻能通過終結器訪問該對象
  • 重映射bit – 引用指向對象的當前地址(請參閱重定位)
  • marked0marked1bits – 用於標記對象可達性

我們還將這些位稱為元數據位。在ZGC中,這些元數據位中隻有一個是1。

3.2。 Multi-Mapping

Multi-mapping意味著我們將多個虛擬內存範圍映射到物理內存。在ZGC中,這些範圍僅在前麵提到的元數據位中不同。

請注意指針著色解除引用更加昂貴因為我們必須屏蔽有用位來訪問地址本身。然而,ZGC繞過這個成本,因為四個元數據位中隻有一個是1。這樣我們隻有四個範圍要映射,映射由操作係統處理。此外,我們隻使用其中三個範圍,因為我們從不想取消引用可終結指針:

3.3。Load Barriers

Load barriers是一段運行代碼(當線程從堆中載入引用時) – 例如,當我們訪問對象的non-primitive字段時。

在ZGC中,load barriers檢查引用的元數據位。根據這些位,ZGC可能會在獲得引用前執行一些處理。因此,它可能產生完全不同的引用。

3.4。標記

標記是垃圾收集器確定對象是否可達的過程。不可達對象被認為是需要回收的對象。 ZGC將標記分為三個階段:

第一階段是Stop-the-world階段。在這個階段,我們尋找根引用並標記它們。根引用是到達堆中對象的起點例如,局部變量或靜態字段。由於根引用的數量通常較小,因此該階段很短。

下一階段是並發階段。在這個階段,我們遍曆對象圖,從根引用開始。我們標記我們到達的每個對象。此外,當Load Barriers檢測到未標記的引用時,也會標記它。

最後階段也是stop-the-world階段,用於處理一些邊緣問題,比如弱引用。

此時,我們知道哪些對象是可達的。

ZGC使用marked0marked1用於標記的元數據位。

3.5。重定位

當我們必須為新對象分配內存時,我們可以遵循兩種策略。

首先,我們可以掃描內存中的可用空間,該空間足以容納我們的對象。掃描內存是一項昂貴的操作。此外,內存將被分段,因為對象之間會有間隙。如果我們想要最小化這些差距,它會使用更多的處理能力。

另一個策略是經常將碎片存儲區中的對象重定位到更緊湊格式的空閑區域。為了更有效,我們將內存空間分成塊。我們重新定位塊中的所有對象或不重定位。這樣,內存分配會更快,因為我們知道內存中有整個空塊。

在ZGC,重定位也包括三個階段。

  1. 並發階段:查找我們要重定位的塊並將它們放入重定位集中。
  2. Stop-the-world階段:重定位重定位集中的所有根引用並更新其引用。
  3. 並發階段:重定位重定位集中的所有剩餘對象,並在轉發表中存儲舊地址和新地址之間的映射。

3.6。重新映射

請注意,在重定位階段,我們沒有重寫對重定位對象的所有引用。因此,使用這些引用,我們將無法訪問我們想要的對象。更糟糕的是,我們可能會訪問垃圾。

ZGC使用load barriers來解決這個問題。Load barriers使用稱為重新映射的技術來修複指向重定位對象的引用。

下圖顯示了重映射的工作原理:

4.如何啟用ZGC?

運行我們的應用程序時,我們可以使用以下命令行選項啟用ZGC:

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

請注意,由於ZGC是一個實驗性GC,因此需要一些時間才能獲得官方支持。

5.結論

在本文中,我們看到ZGC打算以較低的應用程序暫停時間支持超大堆內存GC管理。

為了實現這一目標,它使用了包括彩色64位指針,Load barriers,重定位和重新映射在內的技術。

參考資料

本文由《純淨天空》出品。文章地址: https://vimsky.com/zh-tw/article/4162.html,未經允許,請勿轉載。