[轉帖]深入JVM - Code Cache記憶體池

济南小老虎發表於2024-05-05
https://juejin.cn/post/6985913007142354958

1. 本文內容

本文簡要介紹JVM的 Code Cache(原生代碼快取池)。

2. Code Cache 簡要介紹

簡單來說,JVM會將位元組碼編譯為本地機器碼,並使用 Code Cache 來儲存。 每一個可執行的原生代碼塊,稱為一個 nmethod。 nmethod 可能對應一個完整的Java方法,或者是內聯後的方法。

即時編譯器(just-in-time,JIT)是程式碼快取區的最大消費者,所以此區域又被開發者稱為 JIT code cache。

3. 對 Code Cache 進行調優

code cache 區域的大小是固定的。

如果Code Cache區域用滿了,就會停止JIT編譯, 也就是說JVM不再編譯任何程式碼。 我們還會收到 "CodeCache is full… The compiler has been disabled" 之類的告警訊息。 JIT編譯器關閉的結果,就是系統效能急劇下降。 為了避免這種情況,我們需要對Code Cache進行調優,例如使用以下引數:

  • InitialCodeCacheSize - 初始大小, 預設值為 160KB
  • ReservedCodeCacheSize - 保留給Code Cache的空間, 也就是最大空間, 預設值: 48MB
  • CodeCacheExpansionSize - 每次擴充的大小, 一般為 32KB 或者 64KB

合理地增加 ReservedCodeCacheSize 是一種解決辦法, 畢竟現在很多應用加上依賴庫的程式碼量一點都不少。 但我們也不能無限制地增大這個區域的大小。

幸運的是,JVM提供了一個啟動引數 UseCodeCacheFlushing, 用來控制Code Cache的重新整理。 這個引數的預設值為 false。 如果將其開啟(-XX:+UseCodeCacheFlushing),則會在滿足以下條件時釋放佔用的區域:

  • code cache用滿; 如果該區域的大小超過某個閾值,則會重新整理。
  • 自上次清理後經過了一定的時間間隔。
  • 預編譯的程式碼不夠熱。 對於每個JIT編譯的方法,JVM都會有一個熱度跟蹤計數器。 如果計數器的值小於動態閾值,則JVM會釋放這段預編譯的程式碼。

提示: 除非Code Cache不夠用了,否則不要亂開;

4. 檢視Code Cache的使用情況

想要監控程式碼快取的使用情況,我們可以跟蹤當前使用的記憶體大小。

指定JVM啟動引數: -XX:+PrintCodeCache, 會列印Code Cache區的使用情況。 程式執行過程中, 我們可以看到類似下面的輸出:

shell
複製程式碼
$ java -XX:+PrintCodeCache -XX:+UseCodeCacheFlushing -version

CodeCache: size=245760Kb used=1060Kb max_used=1071Kb free=244699Kb

一起來分析下各個部分數值的含義:

  • size 表示此記憶體區域的最大值,與 ReservedCodeCacheSize 相等。
  • used 是此區域當前實際使用的記憶體大小。
  • max_used 是程式啟動以來的歷史最大使用量
  • free 是此區域尚未使用的空閒空間

PrintCodeCache 選項非常有用,可以幫助我們:

  • 檢視何時進行了重新整理(flushing)
  • 確定記憶體使用量是否達到關鍵點位

5. Code Cache分段

從Java 9開始,JVM將 Code Cache 細分為三個不同的段,每個段包含一種型別的編譯程式碼。 具體是:

  • 非方法段(non-method segment), 儲存相關的JVM內部程式碼,例如位元組碼直譯器。 預設情況下,此段約為 5 MB。 可透過 -XX:NonNMethodCodeHeapSize 引數進行調整。
  • 待分析程式碼段(profiled-code segment), 包含經過簡單最佳化的程式碼,使用壽命很短。 此段的大小預設為 122 MB,可以透過 -XX:ProfiledCodeHeapSize 引數進行調整。
  • 靜態程式碼段(non-profiled segment), 儲存經過全面最佳化的原生代碼,使用壽命可能很長。 預設大小同樣是 122 MB。 可以透過-XX:NonProfiledCodeHeapSize 引數進行調整。

這種新的分段結構,以不同方式處理各種型別的編譯程式碼,整體上具有更好的效能。

例如,將已編譯的短命程式碼和長壽程式碼分開,提高方法清除器的效能 - 畢竟需要掃描的記憶體區域變小了。

6. 小結

本文簡要介紹了JVM的Code Cache記憶體區域。

也介紹了一些監視和診斷此記憶體區使用情況的方法,以及相關的最佳化和配置選項。

  • 原文連結: www.baeldung.com/jvm-code-ca…
  • GitHub雙語對照版: github.com/cncounter/t… (路過的小夥伴, 請點小星星Star支援)

作者:鐵錨
連結:https://juejin.cn/post/6985913007142354958
來源:稀土掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

相關文章