原生镜像
原生镜像是一种将 Java 代码提前编译为二进制文件(原生可执行文件)的技术。原生可执行文件仅包含运行时所需的代码,即应用程序类、标准库类、语言运行时以及来自 JDK 的静态链接的原生代码。
原生镜像构建器(或 native-image
)会创建可执行文件,它具有以下几个重要优势:
- 它使用的资源仅为 Java 虚拟机所需资源的一小部分,因此运行成本更低。
- 启动时间为毫秒级。
- 立即提供峰值性能,无需预热。
- 可以打包到轻量级容器镜像中,实现快速高效的部署。
- 攻击面减少。
原生镜像构建器或 native-image
会处理您的应用程序类以及 其他元数据,为特定的操作系统和架构创建二进制文件,从而生成原生可执行文件。首先,native-image
工具会对您的代码进行静态分析,以确定您的应用程序运行时可达的类和方法。其次,它会将类、方法和资源编译为二进制文件。整个过程称为构建时间,以便与将 Java 源代码编译为字节码的过程区别开来。
native-image
工具可用于构建原生可执行文件(默认)或原生共享库。本快速入门指南重点介绍如何构建原生可执行文件;要了解有关原生共享库的更多信息,请访问 这里。
为了熟悉原生镜像术语并更好地了解该技术,我们建议您阅读 原生镜像基础。
目录 #
先决条件 #
native-image
工具位于 GraalVM 安装目录的 bin
目录中,它依赖于本地工具链(C 库的头文件、glibc-devel
、zlib
、gcc
和/或 libstdc++-static
)。这些依赖项可以使用您计算机上的包管理器安装(如果尚未安装)。选择您的操作系统以查找满足先决条件的说明。
Linux
在 Oracle Linux 上,使用 yum
包管理器
sudo yum install gcc glibc-devel zlib-devel
某些 Linux 发行版可能还需要 libstdc++-static
。如果启用了可选存储库(Oracle Linux 7 上的 ol7_optional_latest、Oracle Linux 8 上的 ol8_codeready_builder 以及 Oracle Linux 9 上的 ol9_codeready_builder),您可以安装 libstdc++-static
。
在 Ubuntu Linux 上,使用 apt-get
包管理器
sudo apt-get install build-essential zlib1g-dev
在其他 Linux 发行版上,使用 dnf
包管理器
sudo dnf install gcc glibc-devel zlib-devel libstdc++-static
MacOS
在 macOS 上,使用 xcode
xcode-select --install
Windows
要在 Windows 上使用原生镜像,请安装 Visual Studio 2022 版本 17.6.0 或更高版本,以及 Microsoft Visual C++ (MSVC)。有两种安装选项
- 安装包含 Windows 11 SDK(或更高版本)的 Visual Studio 构建工具
- 安装包含 Windows 11 SDK(或更高版本)的 Visual Studio
原生镜像可在 PowerShell 或命令提示符中运行,它会自动在 Windows 上设置构建环境,前提是它能找到合适的 Visual Studio 安装。
有关更多信息,请参阅 在 Windows 上使用 GraalVM 和原生镜像。
使用 Maven 或 Gradle 构建原生可执行文件 #
我们提供面向原生镜像的 Maven 和 Gradle 插件,用于自动构建、测试和配置原生可执行文件。
Maven #
面向原生镜像的 Maven 插件 添加了对使用 Apache Maven 将 Java 应用程序编译为原生可执行文件的支持。
- 在您喜欢的 IDE 或终端中使用以下结构创建一个名为“helloworld”的新 Maven Java 项目
├── pom.xml └── src ├── main │ └── java │ └── com │ └── example │ └── App.java
例如,您可以运行以下命令使用快速入门原型创建一个新的 Maven 项目
mvn archetype:generate -DgroupId=com.example -DartifactId=helloworld -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
- 将用于编译和将项目组装为可执行 JAR 文件的常规 Maven 插件添加到您的 pom.xml 文件中
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.12.1</version> <configuration> <fork>true</fork> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <mainClass>com.example.App</mainClass> <addClasspath>true</addClasspath> </manifest> </archive> </configuration> </plugin> </plugins> </build>
- 通过将以下配置文件添加到 pom.xml 文件中,启用面向原生镜像的 Maven 插件
<profiles> <profile> <id>native</id> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <version>${native.maven.plugin.version}</version> <extensions>true</extensions> <executions> <execution> <id>build-native</id> <goals> <goal>compile-no-fork</goal> </goals> <phase>package</phase> </execution> <execution> <id>test-native</id> <goals> <goal>test</goal> </goals> <phase>test</phase> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles>
将
version
属性设置为最新的插件版本(例如,通过在<properties>
元素中指定版本,如<native.maven.plugin.version>
)。 - 编译项目并构建原生可执行文件,这两项操作只需一步即可完成
mvn -Pnative package
原生可执行文件(名为
helloworld
)将创建在项目目录的 target/ 目录中。 - 运行可执行文件
./target/helloworld
就是这样,您已成功使用 Maven 为您的 Java 应用程序创建了原生可执行文件。
面向原生镜像构建的 Maven 插件提供了许多其他功能,这些功能对于更复杂的应用程序可能需要,例如资源自动检测、生成所需的配置、在原生可执行文件中运行 JUnit Platform 测试等,在 插件参考文档 中有详细描述。
Gradle #
面向原生镜像的 Gradle 插件 添加了对使用 Gradle 构建工具 将 Java 应用程序编译为原生可执行文件的支持。
- 在您喜欢的 IDE 或终端中使用以下结构创建一个名为“helloworld”的新 Gradle Java 项目
├── app │ ├── build.gradle │ └── src │ ├── main │ │ ├── java │ │ │ └── org │ │ │ └── example │ │ │ └── App.java │ │ └── resources
例如,使用
java
插件初始化一个新的 Gradle 项目- 创建一个新目录并进入该目录
mkdir helloworld && cd helloworld
- 生成一个项目
gradle init --project-name helloworld --type java-application --test-framework junit-jupiter --dsl groovy
按照提示操作。该命令将设置一个新的 Java 应用程序,包括必要的目录结构和构建文件。
- 创建一个新目录并进入该目录
- 通过将以下内容添加到项目 build.gradle 文件的
plugins
部分,启用面向原生镜像的 Gradle 插件plugins { // ... id 'org.graalvm.buildtools.native' version 'x.x.x' }
为
'x.x.x'
版本值指定最新的插件版本。 - 通过运行
./gradlew nativeCompile
构建原生可执行文件./gradlew nativeCompile
原生可执行文件(名为
app
)将创建在项目目录的 app/build/native/nativeCompile/ 目录中。 - 运行原生可执行文件
./app/build/native/nativeCompile/app
就是这样,您已成功使用 Gradle 为您的 Java 应用程序创建了原生可执行文件。
面向原生镜像构建的 Gradle 插件提供了许多其他功能,这些功能对于更复杂的应用程序可能需要,例如资源自动检测、生成所需的配置、在原生可执行文件中运行 JUnit Platform 测试等,在 插件参考文档 中有详细描述。
使用 native-image
工具构建原生可执行文件 #
native-image
工具将 Java 字节码作为输入。您可以从类文件、JAR 文件或模块(使用 Java 9 及更高版本)构建原生可执行文件。
从类构建 #
要从当前工作目录中的 Java 类文件构建原生可执行文件,请使用以下命令
native-image [options] class [imagename] [options]
例如,为 HelloWorld 应用程序构建原生可执行文件。
- 将此代码保存到名为 HelloWorld.java 的文件中
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, Native World!"); } }
- 编译它并从 Java 类构建原生可执行文件
javac HelloWorld.java native-image HelloWorld
它将在当前工作目录中创建一个名为
helloworld
的原生可执行文件。 -
运行应用程序
./helloworld
您可以计时以查看所使用的资源
time -f 'Elapsed Time: %e s Max RSS: %M KB' ./helloworld # Hello, Native World! # Elapsed Time: 0.00 s Max RSS: 7620 KB
从 JAR 文件构建 #
要从当前工作目录中的 JAR 文件构建原生可执行文件,请使用以下命令
native-image [options] -jar jarfile [imagename]
native-image
的默认行为与 java
命令一致,这意味着您可以传递 -jar
、-cp
、-m
选项来使用原生镜像进行构建,就像您通常使用 java
一样。例如,java -jar App.jar someArgument
将变为 native-image -jar App.jar
,而 ./App someArgument
则保持不变。
请按照本指南 从 JAR 文件构建原生可执行文件。
从模块构建 #
您还可以将模块化的 Java 应用程序转换为原生可执行文件。
从 Java 模块构建原生可执行文件的命令为
native-image [options] --module <module>[/<mainclass>] [options]
有关如何从模块化的 Java 应用程序生成原生可执行文件的更多信息,请参阅 将 HelloWorld Java 模块构建为原生可执行文件。
构建配置 #
您可以向 native-image
工具传递许多选项来配置构建过程。运行 native-image --help
查看完整列表。传递给 native-image
的选项将从左到右进行评估。
有关不同的构建调整以及构建时间配置的更多信息,请参阅 原生镜像构建配置。
原生镜像将在构建过程中输出进度和各种统计信息。要了解有关输出和不同构建阶段的更多信息,请参阅 构建输出。有关原生可执行文件内容的更详细见解,请参阅 构建报告。
原生镜像和第三方库 #
对于使用外部库的更复杂应用程序,您必须向 native-image
工具提供元数据。
使用 native-image
构建独立二进制文件是在“封闭世界假设”下进行的。native-image
工具会执行分析以查看您的应用程序中哪些类、方法和字段是可达的,必须包含在原生可执行文件中。该分析是静态的:它不会运行您的应用程序。这意味着在构建时必须知道(观察和分析)在运行时可以调用的应用程序中的所有字节码。
该分析可以确定某些动态类加载情况,但它不能总是彻底预测 Java 本地接口 (JNI)、Java 反射、动态代理对象或类路径资源的所有用法。要处理 Java 的这些动态功能,您可以向分析提供使用反射、代理等的类的详细信息,或要动态加载的类。为此,您需要向 native-image
工具提供 JSON 格式的配置文件,或者在代码中预先计算元数据。
要了解更多关于元数据、提供元数据的方式以及支持的元数据类型的信息,请参阅可达性元数据。要自动收集应用程序的元数据,请参阅自动收集元数据.
某些应用程序可能需要额外的配置才能使用 Native Image 编译。有关详细信息,请参阅Native Image 兼容性指南.
Native Image 还可以通过自定义 API 与原生语言进行互操作。使用此 API,您可以指定自定义的原生入口点到您的 Java 应用程序,并将它构建成一个原生共享库。要了解更多信息,请参阅与原生代码的互操作性.
进一步阅读 #
本入门指南适用于新手或使用 Native Image 经验较少的用户。我们强烈建议这些用户查看Native Image 基础知识页面,以便在深入研究之前更好地了解一些关键方面。
查看用户指南,以便更深入地了解 Native Image,查找演示示例,并了解潜在的使用场景。
为了循序渐进的学习过程,请查看 Native Image 的构建概述和构建配置文档。
考虑参加交互式研讨会以获得一些实践经验:前往Luna Labs并搜索“Native Image”。
如果您发现潜在的错误,请在 GitHub 上提交问题.
如果您想为 Native Image 做出贡献,请遵循我们的标准贡献工作流程.