原生記憶體追蹤 (NMT) 與 Native Image

原生記憶體追蹤 (NMT) 是一種服務性功能,用於記錄應用程式的堆外記憶體使用情況。術語「堆外記憶體」有時與「原生記憶體」或「非受管理記憶體」互換使用。這基本上是指任何不由垃圾收集器管理的記憶體。

與 HotSpot JVM 不同,Native Image 主要使用由其垃圾收集器管理的已收集堆上的記憶體。然而,Native Image 仍然在許多地方使用原生記憶體,以避免在受管理堆上分配記憶體。一些範例包括 JFR、垃圾收集器和堆傾印。也可以使用 Unsafe#allocateMemory(long) 在應用程式層級直接請求原生記憶體。

啟用原生記憶體追蹤 #

NMT 支援預設為停用,必須在建置時明確啟用。

若要使用 NMT 建置原生可執行檔,請使用 --enable-monitoring=nmt 選項。如果在建置時包含 NMT,則它會在執行時始終啟用。這與 HotSpot 不同,HotSpot 允許在執行時啟用/停用 NMT。

native-image --enable-monitoring=nmt YourApplication

當從原生可執行檔啟動應用程式時,新增 -XX:+PrintNMTStatistics 會指示 NMT 在應用程式完成時將報告寫入標準輸出。

./yourapplication -XX:+PrintNMTStatistics

效能 #

在 Native Image 上,NMT 的 CPU 和記憶體消耗都相當小。與其他服務性功能 (例如 JFR) 相比,NMT 的額外負荷相對非常小。

NMT 的 JFR 事件 #

Native Image 中支援 OpenJDK JFR 事件 jdk.NativeMemoryUsagejdk.NativeMemoryUsageTotal

您也可以存取兩個 Native Image 特定 JFR 事件:jdk.NativeMemoryUsagePeakjdk.NativeMemoryUsageTotalPeak。建立這些 Native Image 特定事件是為了公開透過從 OpenJDK 移植過來的 JFR 事件未公開的峰值使用資料。這些新事件標示為實驗性。您可能需要在軟體 (例如 JDK Mission Control) 中啟用實驗性事件才能檢視它們。

若要將這些 JFR 事件用於 NMT,請在叫用 native-image 工具時傳遞 --enable-monitoring=jfr,nmt 選項來啟用 JFR 監控,然後在執行時開始 JFR 錄製。(在使用 Native Image 的 JDK Flight Recorder (JFR)中了解更多資訊)。

請參閱下方範例,了解使用 jfr 命令列工具檢視時,新事件的外觀

jfr print --events jdk.NativeMemoryUsagePeak recording.jfr 

jdk.NativeMemoryUsagePeak {
  startTime = 13:18:50.605 (2024-04-30)
  type = "Threading"
  peakReserved = 424 bytes
  peakCommitted = 424 bytes
  countAtPeak = 4
  eventThread = "JFR Shutdown Hook" (javaThreadId = 63)
}

jdk.NativeMemoryUsagePeak {
  startTime = 13:18:50.605 (2024-04-30)
  type = "Unsafe"
  peakReserved = 14.0 kB
  peakCommitted = 14.0 kB
  countAtPeak = 2
  eventThread = "JFR Shutdown Hook" (javaThreadId = 63)
}

限制 #

在 HotSpot 上,NMT 有兩種模式:摘要和詳細。在 Native Image 中,目前僅支援 NMT 摘要模式。無法使用啟用呼叫站追蹤的詳細模式。也尚無法擷取基準。如果您對支援這些其他功能感興趣,請向 GitHub 上的 GraalVM 專案提交請求。

目前僅提供 Malloc 追蹤功能 (截至適用於 JDK 23 的 GraalVM)。

Native Image 與 HotSpot 相同,只能追蹤在 VM 層級和使用 Unsafe#allocateMemory(long) 進行的分配。例如,如果程式庫程式碼或應用程式程式碼直接呼叫 malloc,該呼叫會略過 NMT 會計,且不會被追蹤。

延伸閱讀 #

與我們聯繫