- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
Native Image 兼容性指南
Native Image 编译 Java 应用程序的方式与传统 Java 虚拟机 (VM) 不同。它区分了构建时和运行时。在镜像构建时,native-image
构建器执行静态分析,以查找应用程序入口点可访问的所有方法。然后,构建器将这些(且仅这些)方法编译成一个可执行二进制文件。由于这种不同的编译模型,Java 应用程序在编译为原生镜像时行为可能有所不同。
Native Image 提供了一种优化,可减少应用程序的内存占用和启动时间。这种方法依赖于“封闭世界假设”,其中所有代码在构建时都是已知的。也就是说,在运行时不会加载新代码。与大多数优化一样,并非所有应用程序都适用于这种方法。如果 native-image
构建器无法在构建时优化应用程序,它会生成一个所谓的“回退文件”,该文件需要 Java VM 才能运行。我们建议查看Native Image 基础,以详细了解您的 Java 应用程序在构建时和运行时的行为。
需要元数据的功能 #
为了适应封闭世界假设,以下 Java 功能通常需要元数据在构建时传递给 native-image
。此元数据可确保原生镜像使用所需的最小空间量。
通过在 GitHub 上发布共享可达性元数据,Native Image 与最流行的 Java 库的兼容性最近得到了增强。用户可以分担维护第三方依赖项元数据的负担并重用它。请参阅可达性元数据了解更多信息。
与封闭世界假设不兼容的功能 #
一些 Java 功能在封闭世界假设下尚不支持,如果使用,将导致生成回退文件。
invokedynamic
字节码和方法句柄 #
在封闭世界假设下,所有被调用的方法及其调用站点都必须是已知的。invokedynamic
方法和方法句柄可以在运行时引入调用或更改被调用的方法。
请注意,由 javac
生成的 invokedynamic
用例,例如 Java lambda 表达式和字符串拼接,是受支持的,因为它们在运行时不会更改被调用的方法。
在原生镜像中可能操作不同的功能 #
Native Image 实现了一些 Java 功能,其方式与 Java VM 不同。
安全管理器 #
java.lang.System#getSecurityManager()
始终返回 null
,即使在启动时通过 -Djava.security.manager
设置了安全管理器。
如果程序启动时 -Djava.security.manager
被设置为除 disallow
以外的任何值,则使用非空参数调用的 java.lang.System#setSecurityManager(SecurityManager)
将抛出 java.lang.SecurityException
。
信号处理器 #
注册信号处理器需要启动一个新线程来处理信号并调用关机钩子。默认情况下,在构建原生镜像时不会注册信号处理器,除非用户明确注册。例如,在构建共享库时不建议注册默认信号处理器,但在为容器化环境(如 Docker 容器)构建原生可执行文件时,包含信号处理器是可取的。
要注册默认信号处理器,请将 --install-exit-handlers
选项传递给 native-image
构建器。此选项为您提供与 Java VM 相同的信号处理器。
类初始化器 #
默认情况下,类在运行时初始化。这确保了兼容性,但限制了一些优化。为了更快的启动和更好的峰值性能,最好在构建时初始化类。类初始化行为可以使用选项 --initialize-at-build-time
或 --initialize-at-run-time
来指定特定类和包或所有类。JDK 类库中的类默认初始化。
注意:在构建时初始化类可能会破坏现有代码中的特定假设。例如,在类初始化器中加载的文件在构建时可能与运行时不在同一位置。此外,某些对象(如文件描述符或运行中的线程)不得存储在原生可执行文件中。如果在构建时可访问此类对象,则 native image
构建器将失败并报错。
更多信息,请参阅Native Image 中的类初始化。
终结器 #
Java 基类 java.lang.Object
定义了 finalize()
方法。当垃圾回收器确定没有更多引用指向某个对象时,它会在该对象上调用此方法。子类可以覆盖 finalize()
方法以释放系统资源或执行其他清理操作。
自 Java SE 9 以来,终结器已被弃用。它们实现起来复杂,并且语义设计不良。例如,终结器可以通过将引用存储在静态字段中而使对象再次可达。因此,终结器不会被调用。我们建议您用弱引用和引用队列替换终结器。
线程 #
Native Image 不实现 java.lang.Thread
中早已弃用的方法,例如 Thread.stop()
。
不安全内存访问 #
如果类在构建时初始化,则使用 sun.misc.Unsafe
访问的字段需要为静态分析明确标记。在大多数情况下,这会自动发生:存储在 static final
字段中的字段偏移量会自动从托管值(native image
构建器运行的 Java VM 的字段偏移量)重写为原生可执行值,作为重写的一部分,该字段被标记为 Unsafe
访问。对于非标准模式,字段偏移量可以使用注解 RecomputeFieldValue
手动重新计算。
调试和监控 #
Java 有一些可选的规范,Java 实现可以用于调试和监控 Java 程序,包括 JVMTI。它们帮助您在运行时监控 Java VM 的事件,例如编译,这些事件在大多数原生镜像中不会发生。这些接口建立在 Java 字节码在运行时可用的假设之上,而对于使用封闭世界优化构建的原生镜像来说并非如此。由于 native-image
构建器生成原生可执行文件,用户必须使用原生调试器和监控工具(如 GDB 或 VTune),而不是针对 Java 的工具。JVMTI 和其他基于字节码的工具不支持 Native Image。
Linux AArch64 架构上的限制
大多数 Native Image 功能在 Linux AArch64 架构上都受支持,但以下限制除外。
-R:[+|-]WriteableCodeCache
:必须禁用。--libc=<value>
:不支持musl
。
在此处查找 native-image
构建器的选项列表:Options。