JDK17中關於ZGC的部分最佳化建議

吉姆雷诺發表於2024-11-26

ZGC是一個可擴充套件的低延遲垃圾回收器。ZGC 在執行所有耗時操作時都是併發進行的,不會停止應用執行緒的執行超過一毫秒。它適用於需要低延遲的應用程式。暫停時間與使用的堆大小無關。ZGC 對於從幾百兆位元組到 16TB 的堆大小都能良好工作。

ZGC版本的選擇

ZGC存在兩種版本,舊版本是不使用分代演算法,新版本使用分代演算法。
如果開啟ZGC:
使用新版本的,分代的ZGC:

-XX:+UseZGC -XX:+ZGenerational -Xmx<size> -Xlog:gc*

使用舊版本,不使用分代的ZGC:

-XX:+UseZGC -Xmx<size> -Xlog:gc*

ZGC的常見最佳化

1 使用大頁(Large Pages)

配置ZGC使用大頁,通常會在吞吐量、延遲和啟動時間等方面效能有不錯的表現,而且並沒有明顯的缺點。
但是使用大頁,通常需要使用Root許可權。

大頁是指比常規的 4KB 頁面更大的記憶體頁面,通常是 2MB 或 1GB 大小(這取決於系統的硬體架構和配置)。它們用於減少記憶體管理的開銷,提高記憶體訪問效率。使用大頁時,作業系統將記憶體劃分為更大的塊來減少頁面管理和對映的開銷。

以Linux為例。
對於Linux x86, 大頁的大小為 2MB.
假如我要設定堆使用16G,但是我需要至少留夠2G提供給非堆記憶體使用。因此一共需要18G。
首先,我要配置Linux系統的大頁池有足夠的要求:

18GB ÷ 2MB = 9,216 huge pages
所以需要 9216 個huge pages。

$ echo 9216 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

受限於系統要求,可能不會成功,而且需要花費較多時間。

執行完成後,可以使用以下命令進行檢查:

$ cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

啟動JVM時,新增配置引數:

-XX:+UseLargePages

2 使用透明大頁

當kernel >= 4.7版本時,可以使用透明大頁。

透明大頁是大頁技術的一種自動化實現,它讓大頁的使用對應用程式透明。也就是說,使用者和應用程式不需要做任何特殊配置,核心會自動決定何時使用大頁,並在需要時將大頁對映到記憶體中。THP 使得應用程式能夠從大頁中受益,而不需要手動管理大頁記憶體。

開啟引數:

-XX:+UseLargePages -XX:+UseTransparentHugePages

前提是要現在Linux中開啟透明大頁的:

$ echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

madvise() 是一個系統呼叫,用於提供記憶體建議,以最佳化記憶體管理。在 Linux 中,madvise 系統呼叫允許程序向作業系統核心提供有關如何處理記憶體區域的建議。透過這些建議,核心可以更高效地管理記憶體,從而提高應用程式的效能或降低記憶體佔用。

ZGC 使用shmem大頁作為堆記憶體,所以還需要進行配置,將共享記憶體區域配置為使用大頁面:

echo advise > /sys/kernel/mm/transparent_hugepage/shmem_enabled

共享記憶體(shmem)是一個特殊的記憶體區域,可以被多個程序共享。這使得多個程序可以訪問相同的記憶體區域,避免了重複的記憶體分配,提高了記憶體的使用效率和效能。共享記憶體通常用於程序間通訊(IPC)和快取的共享。

透明大頁的相關文件

3 啟用NUMA支援

NUMA(Non-Uniform Memory Access) 是一種多處理器計算架構,旨在提高具有多個處理器(CPU)系統的記憶體訪問效率。在 NUMA 架構中,計算機系統的記憶體被分為多個區域或“節點”,每個處理器都與一個或多個記憶體區域(節點)直接關聯。與傳統的對稱多處理器(SMP)系統不同,NUMA 允許每個處理器擁有自己的本地記憶體區域,並且也能夠訪問其他處理器的記憶體區域,但訪問其他記憶體區域的速度會比較慢。

ZGC支援NUMA,會嘗試把Java堆分配到NUMA的本地記憶體。這個是預設開啟的。
如果你要明確地開啟:

XX:+UseNUMA

4 可以使用GC日誌

啟用引數:

-Xlog:gc*:gc.log

gc* 把開頭帶gc的日誌都寫入
gc.log 表示把日誌寫入名為gc.log的檔案

5 歸還未使用的記憶體給作業系統

通常不建議使用該功能。
這個功能對於關注程式和環境非常有用。但是對Java執行緒的延遲有不好的影響。
可以透過以下命令關閉:

-XX:-ZUncommit

6 設定堆大小

ZGC最重要的調整,就是調整Java堆的大小。可以透過-Xmx進行調整。
因為ZGC是一個併發收集器, 所以必須要有足夠的堆大小可以容納程式的生產大小。通常來說,設定越大的記憶體肯定是越好的。但是,並不建議浪費太多的記憶體。
ZGC有一個有意思的配置,-XX:SoftMaxHeapSize。可以使用該配置軟設定一個Java堆可以使用的大小。如:

 -Xmx5g -XX:SoftMaxHeapSize=4g

在這個例子裡,ZGC 將使用 4GB 作為其限制,但如果它無法將堆大小保持在 4GB 以下,它仍然允許臨時使用最多5GB。

相關文章