GraalVM将Java程序构建为原生可执行文件(Native Image)过程
使用 GraalVM 将 Java 程序构建为原生可执行文件(Native Image)的过程,核心原理是利用提前编译(AOT, Ahead-of-Time Compilation)。它将 Java 字节码直接编译成特定操作系统和架构的机器码,运行时不再需要完整的 JVM。
1. 环境准备
要在本地构建 Native Image,除了 GraalVM 本身,还需要安装目标系统底层的 C++ 编译工具链。
1.1 安装 GraalVM
- 前往 GraalVM 官网或使用环境管理工具(如
sdkman)下载并安装对应 Java 版本的 GraalVM。
- 前往 GraalVM 官网或使用环境管理工具(如
- 配置环境变量
JAVA_HOME指向 GraalVM 的安装目录,并将bin目录添加到PATH中。
- 配置环境变量
- 注意:从 GraalVM 22.3 开始,
native-image工具已经默认内置,无需再像旧版本那样执行gu install native-image。
- 注意:从 GraalVM 22.3 开始,
#1.mac execute(挂梯子)
curl -s "https://get.sdkman.io" | zsh
tee >>~/.zshrc <<'EOF'
source "$HOME/.sdkman/bin/sdkman-init.sh"
EOF
source ~/.zshrc
#2.install java-graal && maven
sdk install java 24-graal
sdk install maven
#3.check
java --version && mvn --version
1.2 安装本地编译工具链
根据你的操作系统安装相应的依赖:
- Linux:
1 | # Ubuntu/Debian |
- macOS:
安装 Xcode 命令行工具:
1 | xcode-select --install |
- Windows:
- 安装 Visual Studio(推荐 2022 社区版),并在安装时勾选 “使用 C++ 的桌面开发”。
- 关键步骤:后续所有构建命令,都必须在 Visual Studio 自带的 **”Developer Command Prompt for VS”**(开发者命令提示符)中执行,否则会找不到
cl.exe编译器。
2. 基础构建示例(命令行方式)
第一步:编写 Java 代码
创建一个名为 HelloWorld.java 的文件:
1 | public class HelloWorld { |
第二步:编译为字节码
使用 GraalVM 的 javac 将其编译为 .class 文件:
1 | javac HelloWorld.java |
第三步:构建原生可执行文件
使用 native-image 命令进行 AOT 编译:
1 | native-image HelloWorld |
构建过程会消耗较多的内存和 CPU,通常需要数十秒到数分钟。
构建成功后,你会在当前目录下看到一个无后缀(Linux/macOS)或 .exe 结尾(Windows)的二进制文件,例如 helloworld。
第四步:运行原生程序
此时你不再依赖 java -jar 命令,直接运行该文件:
1 | ./helloworld |
3. 实际项目构建(使用 Maven/Gradle)
在实际开发中,我们通常使用 Maven 或 Gradle 来管理复杂的依赖并构建原生镜像。
3.1 使用 Maven 构建
GraalVM 提供了 native-maven-plugin 插件。在 pom.xml 中添加以下配置:
1 | <project> |
执行构建命令:
1 | mvn -Pnative package |
构建完成后,原生可执行文件将生成在 target/ 目录下。
3.2 使用 Gradle 构建
在 build.gradle 中引入 GraalVM Native Image 插件:
1 | plugins { |
执行构建命令:
1 | ./gradlew nativeCompile |
构建完成后,可执行文件位于 build/native/nativeCompile/ 目录下。
4. 核心注意事项与局限性(封闭世界假设)
GraalVM Native Image 遵循封闭世界假设(Closed-World Assumption):在编译时,所有可达的代码、类、方法和属性都必须是已知且确定的。
因此,Java 的一些动态特性在默认情况下无法在原生镜像中直接使用,需要特殊处理:
- 反射(Reflection)与动态代理(Dynamic Proxy):如果代码中使用了反射,必须在编译时通过 JSON 配置文件(如
reflect-config.json)告诉 GraalVM 哪些类会被反射调用。 - 类路径资源(Resources):读取
src/main/resources下的文件需要提前在resource-config.json中声明。 - 解决方案——使用辅助 Agent:
如果你不确定项目用了哪些反射,可以在正常的 JVM 下运行程序并附加 GraalVM 的 Agent,它会自动跟踪并生成所需的配置文件:
1 | java -agentlib:native-image-agent=config-output-dir=/path/to/config -jar my-app.jar |
然后将生成的配置文件放入项目的 META-INF/native-image/ 目录中再进行构建。
💡 提示:现代 Java 框架(如 Spring Boot 3.x、Quarkus、Micronaut)已经原生深度整合了 GraalVM,它们会自动处理大部分反射和 AOT 配置。如果你正在开发新项目,强烈建议使用这些现代化框架来降低构建 Native Image 的门槛。