Java 21 中的分代 ZGC
1. 概述
Java 21 於 2023 年 9 月首次亮相,同時引入了分代 ZGC 。以 Z 垃圾收集器的效率為基礎,此更新側重於透過為新舊物件引入單獨的代來優化記憶體管理。
在本文中,我們將仔細研究這項附加功能,探索其潛在好處、其工作原理以及如何使用它。
2. 垃圾收集
為了開始我們的探索,讓我們深入研究記憶體管理領域。垃圾收集是程式嘗試釋放物件不再使用的已分配記憶體的過程。如果我們程式的某些部分仍然維護指向某個物件的指針,則該物件被視為「正在使用」或「引用」。相反,“未使用”或“未引用”物件不再被程式的任何部分訪問,從而允許回收它佔用的記憶體。
例如,在 Java 中,垃圾收集器負責釋放堆內存,這是儲存 Java 物件的地方。
這有助於防止記憶體洩漏並確保有效的資源使用。它還使我們不必手動管理程式的內存,這可能會導致潛在的錯誤。某些程式語言(例如 Java 或 C#)內建了此功能,而其他程式語言(例如 C 或 C++)可能依賴外部程式庫來實現類似功能。
3. 分代垃圾收集
在記憶體管理的脈絡中,一代是指根據物件的分配時間對物件進行分類。
讓我們將注意力轉移到分代垃圾收集。這代表了一種記憶體管理策略,其工作原理是根據分配時間將物件劃分為不同的代,並根據其代應用不同的方法。
在 Java 上下文中,記憶體被分為兩個主要代:年輕代和老代。新創建的物件在年輕一代中找到自己的位置,在那裡經常進行垃圾收集。超出多個垃圾收集週期的物件將被提升到老一代。這種劃分透過承認大多數物件的壽命較短來優化效率。
有關 Java 中分代垃圾回收的更多信息,請參閱文章Java 垃圾回收基礎知識。
4. Z 垃圾收集器
Z 垃圾收集器也稱為 ZGC,是一種可擴展、低延遲的垃圾收集器。它首先作為實驗性功能在 Java 11 中引入,並在 Java 15 中投入生產。
此功能的目的是最大限度地減少或消除長時間的垃圾收集暫停,從而增強應用程式的響應能力並適應現代系統不斷增長的記憶體容量。
作為一種非分代方法,它將所有物件儲存在一起,無論年齡如何,因此每個週期都會收集所有物件。
5. Z世代垃圾收集器
分代 ZGC旨在提高應用程式效能,透過為新物件和舊物件維護不同的世代來擴展現有的 ZGC。
5.1.動機
對於大多數用例,ZGC 足以**解決與垃圾收集相關的延遲問題。**只要有足夠的可用資源來確保垃圾收集器回收記憶體的速度快於我們的程式消耗記憶體的速度,這種方法就很有效。
然而,弱世代假說指出,大多數物體都會在年輕時死亡。因此,從這些短命的年輕資源中收集和回收記憶體需要更少的運算資源。此過程會快速解鎖更多記憶體。
另一方面,收集已經經歷了多個週期並具有更長壽命的舊物件需要更多的計算資源。然而,透過收集舊物件釋放的內存量相對較少。事實證明,該策略在快速釋放記憶體方面更加有效,有助於提高整體應用程式效能。
5.2.目標
與非分代 ZGC 相比,分代 ZGC 旨在提供一些關鍵優勢:
- 減少分配停滯的風險
- 減少堆記憶體開銷要求
- 降低垃圾收集 CPU 開銷
此外,我們的目標是增加這些優勢,同時保留使用非分代方法已有的優勢:
- 暫停時間低於一毫秒
- 支援高達數 TB 的堆大小
- 最少的手動配置
為了維持最後一點,新的 GC 不需要任何手動配置代的大小、使用的線程數或物件應在年輕代中駐留多長時間。
5.3.描述
分代 ZGC 引入了兩代堆結構:年輕代用於最近的對象,老年代用於長期對象。每一世代都是獨立收集的,優先收集年輕對象。
並發收集與非分代 ZGC 類似,依賴彩色指針、加載屏障和存儲屏障來實現一致的物件圖視圖。彩色指標包含元數據,有助於有效地使用 64 位元物件指標。載入屏障解釋元數據,而儲存屏障處理元資料新增、維護記憶集並將物件標記為活動物件。
5.4.啟用分代ZGC
為了實現平穩過渡,分代 ZGC 將與非分代 ZGC 一起使用。 -XX:UseZGC
命令列選項將選擇非分代 ZGC。要選擇Generational ZGC,我們需要新增-XX:+ZGenerational
選項:
java -XX:+UseZGC -XX:+ZGenerational ...
Generational ZGC 旨在成為未來 Java 版本中的預設 ZGC 。此外,在較晚的版本中,非分代 ZGC 可能會被完全刪除。
5.5.風險
新GC中屏障和彩色指針的整合引入了更高的複雜性,超越了非分代GC。 Generational ZGC 也同時運作兩個垃圾收集器。這些收集器並不完全獨立,因為它們在某些情況下會交互,從而增加了實現的複雜性。
儘管預計它在大多數用例中都會表現出色,但某些工作負載會帶來效能輕微下降的風險。為了解決這個問題,世代 ZGC 的持續發展和優化將由基準和用戶回饋驅動,旨在隨著時間的推移解決和減輕這些已識別的風險。
6. 各代ZGC設計差異
分代 ZGC 引入了一些設計差異,與非分代 ZGC 相比,提高了垃圾收集效率和使用者適應性。
6.1.透過優化屏障增強性能
分代 ZGC 放棄多重映射內存,轉而使用載入和儲存屏障內的明確代碼。為了適應儲存屏障和修訂後的負載屏障責任,Generational ZGC 採用高度最佳化的屏障代碼。利用快速路徑和慢速路徑等技術,優化的屏障可確保應用程式的最大吞吐量和效能,即使在密集的工作負載下也是如此。
6.2.高效的代間指針跟踪
雙緩衝記憶集(為每個老一代區域成對組織)使用點陣圖來有效追蹤代間指標。這種設計選擇有利於應用程式和垃圾收集線程的並發工作,而不需要額外的記憶體屏障,從而使執行更順暢。
6.3.優化的年輕代集合
透過分析年輕代區域的密度,Generational ZGC 選擇性地疏散區域,減少年輕代收集所需的工作。這種優化有助於實現更快、更有效率的垃圾收集週期,從而提高應用程式回應能力。
6.4.靈活處理大型物體
分代 ZGC 透過允許將大型物件分配給年輕代,引入了處理大型物件的靈活性。這消除了對老一代的搶佔分配的需要,從而提高了記憶體效率。現在,如果生命週期較短,則可以在年輕代中收集大型物件;如果生命週期較長,則可以將其有效提升到老年代。
七、結論
正如我們在整篇文章中所了解到的,Java 21 具有一個強大的功能,即分代 ZGC。透過仔細考慮潛在風險並致力於根據用戶回饋進行持續改進,預計將提供更高的效率和回應能力,使其成為 Java 不斷發展的生態系統的寶貴補充。