原生镜像

原生镜像是一种将 Java 代码提前编译为二进制文件(原生可执行文件)的技术。原生可执行文件仅包含运行时所需的代码,即应用程序类、标准库类、语言运行时以及来自 JDK 的静态链接的原生代码。

原生镜像构建器(或 native-image)会创建可执行文件,它具有以下几个重要优势:

  • 它使用的资源仅为 Java 虚拟机所需资源的一小部分,因此运行成本更低。
  • 启动时间为毫秒级。
  • 立即提供峰值性能,无需预热。
  • 可以打包到轻量级容器镜像中,实现快速高效的部署。
  • 攻击面减少。

原生镜像构建器native-image 会处理您的应用程序类以及 其他元数据,为特定的操作系统和架构创建二进制文件,从而生成原生可执行文件。首先,native-image 工具会对您的代码进行静态分析,以确定您的应用程序运行时可达的类和方法。其次,它会将类、方法和资源编译为二进制文件。整个过程称为构建时间,以便与将 Java 源代码编译为字节码的过程区别开来。

native-image 工具可用于构建原生可执行文件(默认)或原生共享库。本快速入门指南重点介绍如何构建原生可执行文件;要了解有关原生共享库的更多信息,请访问 这里

为了熟悉原生镜像术语并更好地了解该技术,我们建议您阅读 原生镜像基础

目录 #

先决条件 #

native-image 工具位于 GraalVM 安装目录的 bin 目录中,它依赖于本地工具链(C 库的头文件、glibc-develzlibgcc 和/或 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 应用程序编译为原生可执行文件的支持。

  1. 在您喜欢的 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
    
  2. 将用于编译和将项目组装为可执行 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>
    
  3. 通过将以下配置文件添加到 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>)。

  4. 编译项目并构建原生可执行文件,这两项操作只需一步即可完成
     mvn -Pnative package
    

    原生可执行文件(名为 helloworld)将创建在项目目录的 target/ 目录中。

  5. 运行可执行文件
     ./target/helloworld 
    

    就是这样,您已成功使用 Maven 为您的 Java 应用程序创建了原生可执行文件。

面向原生镜像构建的 Maven 插件提供了许多其他功能,这些功能对于更复杂的应用程序可能需要,例如资源自动检测、生成所需的配置、在原生可执行文件中运行 JUnit Platform 测试等,在 插件参考文档 中有详细描述。

Gradle #

面向原生镜像的 Gradle 插件 添加了对使用 Gradle 构建工具 将 Java 应用程序编译为原生可执行文件的支持。

  1. 在您喜欢的 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 应用程序,包括必要的目录结构和构建文件。

  2. 通过将以下内容添加到项目 build.gradle 文件的 plugins 部分,启用面向原生镜像的 Gradle 插件
     plugins {
     // ...
     id 'org.graalvm.buildtools.native' version 'x.x.x'
     }
    

    'x.x.x' 版本值指定最新的插件版本。

  3. 通过运行 ./gradlew nativeCompile 构建原生可执行文件
     ./gradlew nativeCompile
    

    原生可执行文件(名为 app)将创建在项目目录的 app/build/native/nativeCompile/ 目录中。

  4. 运行原生可执行文件
     ./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 应用程序构建原生可执行文件。

  1. 将此代码保存到名为 HelloWorld.java 的文件中
     public class HelloWorld {
         public static void main(String[] args) {
             System.out.println("Hello, Native World!");
         }
     }
    
  2. 编译它并从 Java 类构建原生可执行文件
     javac HelloWorld.java
     native-image HelloWorld
    

    它将在当前工作目录中创建一个名为 helloworld 的原生可执行文件。

  3. 运行应用程序

     ./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 做出贡献,请遵循我们的标准贡献工作流程.

联系我们