- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
原生内存跟踪 (NMT) 与 原生镜像
原生内存跟踪 (NMT) 是一项可维护性功能,用于记录应用程序的堆外内存使用情况。“堆外内存”一词有时可与“原生内存”或“非托管内存”互换使用。这本质上是指任何不受垃圾收集器管理的内存。
与 HotSpot JVM 不同,原生镜像主要使用由其垃圾收集器管理的已收集堆上的内存。然而,原生镜像仍会在许多地方使用原生内存,以避免在托管堆上进行分配。例如,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
性能 #
在原生镜像中,NMT 的 CPU 和内存消耗都非常小。与其他可维护性功能(如 JFR)相比,NMT 的开销相对非常小。
NMT 的 JFR 事件 #
原生镜像支持 OpenJDK JFR 事件 jdk.NativeMemoryUsage
和 jdk.NativeMemoryUsageTotal
。
还有两个原生镜像特有的 JFR 事件可以访问:jdk.NativeMemoryUsagePeak
和 jdk.NativeMemoryUsageTotalPeak
。这些原生镜像特有的事件旨在公开通过 OpenJDK 移植的 JFR 事件未能公开的峰值使用数据。这些新事件被标记为实验性。您可能需要在 JDK Mission Control 等软件中启用实验性事件才能查看它们。
要将这些 JFR 事件用于 NMT,请在调用 native-image
工具时传递 --enable-monitoring=jfr,nmt
选项以启用 JFR 监控,然后在运行时启动 JFR 记录。(在 原生镜像中的 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 有两种模式:摘要模式和详细模式。在原生镜像中,目前仅支持 NMT 摘要模式。详细模式(启用调用点跟踪)不可用。捕获基线也尚不可能。如果您对这些额外功能的支持感兴趣,请向 GitHub 上的 GraalVM 项目提交请求。
Malloc 跟踪是目前唯一可用的功能(截至 GraalVM for JDK 23)。
原生镜像,与 HotSpot 相同,只能跟踪 VM 级别的分配以及使用 Unsafe#allocateMemory(long)
进行的分配。例如,如果库代码或应用程序代码直接调用 malloc
,该调用将绕过 NMT 的统计并不会被跟踪。