注:
- Oracle 提供的免费实验室环境中提供了本教程。
- 它使用 Oracle Cloud Infrastructure 身份证明、租户和区间示例值。完成实验室后,请使用特定于您的云环境的这些值替换这些值。
GraalVM Native Image 入门
简介
此实验逐步引导您完成使用 GraalVM Native Image 构建云原生 Java 应用的过程。它的目标读者是具有 Java 知识的开发人员。
GraalVM Native Image 技术可提前将 Java 代码编译为自包含的可执行文件。只有应用程序运行时所需的代码才会添加到可执行文件中。
本机映像生成的可执行文件具有以下几个重要优点:
- 使用 JVM 所需的一小部分资源,因此运行成本更低
- 开始时间(以毫秒为单位)
- 立即提供峰值性能,无需热身
- 可以打包到轻量级容器映像,提升部署速度和效率
- 展示减少的攻击面(在将来的实验室中详述)
许多先进的微服务框架都支持使用 GraalVM Native Image 进行提前编译,包括 Micronaut、Spring、Helidon 和 Quarkus。
此外,还有适用于 Native Image 的 Maven 和 Gradle 插件,因此您可以轻松构建、测试并将 Java 应用程序作为可执行文件运行。
注意:Oracle Cloud Infrastructure (OCI) 无需额外付费即可提供 GraalVM 企业版。
估计的实验室时间:45 分钟
实验室目标
在此实验室中,您可以执行以下任务:
- 连接到 Oracle Cloud 中的远程主机 - 您将在 Oracle Cloud 计算主机上开发应用
- 使用 GraalVM 构建和运行 Java 应用
- 使用 GraalVM Native Image 将 Java 应用转换为可执行文件
- 构建可执行文件以适应 Java 的动态功能
- 使用 Maven GraalVM 插件通过 GraalVM Native Image 构建可执行文件
注意:如果在实验室中看到笔记本电脑图标,则需要执行一些操作,例如输入命令。留意
# This is where we you will need to do something
STEP 1:连接到远程主机并检查开发环境
您的开发环境由远程主机提供:具有 Oracle Linux 8、1 CPU 和 32GB 内存的 OCI 计算实例。
在远程主机准备就绪之前将显示 Luna Labs 桌面环境,这至多需要两分钟。
通过在 Luna Desktop 环境中运行设置脚本,可以连接到远程主机。可通过“资源”选项卡使用此脚本
-
在桌面中,双击 Luna-Lab.html 图标。此时将打开该页面,其中显示特定于您的实验室的 Oracle Cloud Infrastructure 身份证明和信息。
-
将显示资源选项卡。请注意,资源标题旁边的齿轮在计算实例预配到茶云时会旋转。
-
预配实例时,这至多需要 2 分钟,您将在资源选项卡上看到以下内容
-
从“资源”选项卡复制设置 VS 代码环境的配置脚本。单击查看详细信息链接可显示配置。复制此项,如下面的屏幕截图中所示。
-
打开终端,如下面的屏幕截图中所示:
-
将配置代码粘贴到终端中,终端将打开 VS 代码。
结束了!祝贺您,您现在已成功连接到 Oracle Cloud 中的远程主机!
关于开发环境的说明
您将使用 GraalVM Enterprise 21 作为该实验的 Java 平台。GraalVM 是来自 Oracle 的高性能 JDK 分销商。它基于可信的安全 Oracle Java SE 构建。
您的开发环境预配置了 GraalVM 和本机操作所需的原生映像工具。
您可以通过在终端中运行这些命令轻松检查,可以在 VS Code Terminal > New Terminal
中创建终端:
java -version
native-image --version
步骤 2:构建和运行演示应用程序
我们将使用演示应用程序来展示 GraalVM Native Image:命令行 Java 应用程序,用于计算当前目录及其子目录中的文件数。另外,应用程序还会计算文件的总大小。
远程主机中提供了该应用程序的源代码。
演示应用程序的注释
应用程序由两个 Java 文件组成,这些文件位于 src
目录中:
App.java
:ListDir
类的包装器。ListDir.java
:这将执行所有操作。它对文件进行计数并汇总输出。
应用程序可以手动构建,也可以使用 Maven 配置文件构建。Maven 构建配置由 pom.xml
文件提供。Maven 配置文件是一种在单个 pom.xml
文件中具有不同构建配置的好方法。您可以在此处找到有关 Maven 配置文件的更多信息。
您可以浏览打开的 VS 代码中的文件。
此实验室中将使用多个配置文件,每个配置文件都具有特定用途:
native
:此配置文件使用 GraalVM Native Image 构建可执行文件。java_agent
:此配置文件使用跟踪代理构建 Java 应用程序,该代理跟踪应用程序中动态代码的所有使用情况,并将此信息捕获到配置文件中。稍后详述。
可以使用特定的 Maven 配置文件,将其作为参数传递给 mvn
命令。配置文件的名称附加到 -P
标志后面。可以在 VS 代码内的终端内运行以下命令。
以下示例说明在使用 Maven 构建时如何调用 native
配置文件:
mvn clean package -Pnative
现在,您可以基本了解应用程序运行什么,以查看其运行方式。
-
构建项目,然后从我们在 VS Code 中打开的终端中运行该项目:
mvn clean package exec:exec
以上命令可执行以下操作:
- 清除项目以删除任何已生成或已编译的构件。
- 创建包含应用程序的可运行 JAR 文件。此 JAR 文件稍后将由 Native Image 使用。
- 通过运行
exec
插件运行应用程序。
您应看到生成的输出中包含以下内容(应用程序报告的文件数可能有所不同):
Counting directory: . Total: 15 files, total size = 511.9 KiB [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------
STEP 3:将 Java 应用程序转换为可执行文件
接下来,您将使用 GraalVM Native Image 构建应用的可执行版本。作为快速提醒,GraalVM Native Image 是一种提前编译技术,可将您的 Java 应用转换为自包含的可执行文件,不需要 JDK 运行即可快速启动且高效。
远程主机上预安装了 GraalVM Native Image。
-
首先,检查
target
目录中是否有已编译的 JAR 文件:ls ./target drwxrwxr-x 1 krf krf 4096 Mar 4 11:12 archive-tmp drwxrwxr-x 1 krf krf 4096 Mar 4 11:12 classes drwxrwxr-x 1 krf krf 4096 Mar 4 11:12 generated-sources -rw-rw-r-- 1 krf krf 496273 Mar 4 11:38 graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar -rw-rw-r-- 1 krf krf 7894 Mar 4 11:38 graalvmnidemos-1.0-SNAPSHOT.jar drwxrwxr-x 1 krf krf 4096 Mar 4 11:12 maven-archiver drwxrwxr-x 1 krf krf 4096 Mar 4 11:12 maven-status
您需要的文件是
graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar
。 -
从命令行生成可执行文件。您无需使用 Maven 插件即可使用 GraalVM Native Image,但它可以提供帮助。从项目的根目录
demo
运行以下命令:native-image -jar ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar --no-fallback -H:Class=oracle.App -H:Name=file-count
这将在当前目录中生成名为
file-count
的可执行文件。 -
按如下方式运行此可执行文件:
./file-count
-
现在应用程序的时间。首先将其作为可执行文件运行,然后使用常规
java
命令运行:time ./file-count
time java -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
由
native-image
命令生成的可执行文件运行速度比相应的 Java 应用程序快得多。
让我们更深入地探讨如何创建可执行文件。
在步骤 2 中传递给 native-image
命令的参数指定了什么?
-jar
:指定包含 Java 应用程序的 JAR 文件的位置。(您还可以使用-cp
指定类路径。)--no-fallback
:不生成回退映像。(回退映像需要 JVM 来运行它,您不需要它。)-H:Class
:指定提供入口点方法的类(main
方法)。-H:Name
:指定输出可执行文件的名称。
可以在此处找到完整的文档。
您还可以使用 GraalVM Native Image Maven 插件运行 native-image
工具。项目 pom.xml
(Maven 配置)文件包含以下代码片段,说明了如何使用插件构建可执行文件:
<!-- Native Image -->
<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>build</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<skip>false</skip>
<imageName>${exe.file.name}</imageName>
<mainClass>${app.main.class}</mainClass>
<buildArgs>
<buildArg>--no-fallback</buildArg>
<buildArg>--report-unsupported-elements-at-runtime</buildArg>
</buildArgs>
</configuration>
</plugin>
本机映像 Maven 插件执行生成可执行文件的繁重工作。可以使用 <skip>true</skip>
标记禁用它。另请注意,您可以通过 <buildArgs/>
标记将参数传递给 native-image
。
有关 GraalVM Native Image 插件的完整文档位于此处。
要使用 Maven 配置文件构建可执行文件,请运行:
mvn clean package -Pnative
Maven 构建会将可执行文件 file-count
放置到 target
目录中。
可以按如下方式运行可执行文件:
./target/file-count
STEP 4:使用反射 - 向 Log4J 添加依赖关系
在此步骤中,您将构建可执行文件,该文件与 Java 的动态功能配合使用。
说要向应用程序添加一个依赖于反射的库或一些代码。测试反射的一个好候选对象是 Log4J 日志记录框架。它已作为依赖项添加到项目 pom.xml
文件中:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
要更改应用程序以便使用 log4j
,请编辑 ListDir.java
文件并取消注释几行。
-
使用 VS 代码打开
ListDir.java
文件 -
对声明
log4j
导入的行取消注释,然后取消对以下行的注释://import org.apache.log4j.Logger;
//final static Logger logger = Logger.getLogger(ListDir.class);
/* // Add some logging if(logger.isDebugEnabled()){ logger.debug("Processing : " + dirName); } */
/* // Add some logging if(logger.isDebugEnabled()){ logger.debug("Processing : " + f.getAbsolutePath()); } */
-
保存文件
现在已将日志记录添加到应用程序中,您可以通过重建和运行来查看更改结果。
mvn clean package exec:exec
您应该看到与之前看到的输出类型相同,但添加了更多日志记录。
-
接下来,使用 Maven 配置文件构建可执行文件:
mvn clean package -Pnative
-
运行生成的可执行文件,该文件现在包含日志记录:
./target/file-count
这会产生错误:
Exception in thread "main" java.lang.NoClassDefFoundError at org.apache.log4j.Category.class$(Category.java:118) at org.apache.log4j.Category.<clinit>(Category.java:118) at java.lang.Class.ensureInitialized(DynamicHub.java:552) at oracle.ListDir.<clinit>(ListDir.java:75) at oracle.App.main(App.java:63) Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Category at java.lang.Class.forName(DynamicHub.java:1433) at java.lang.Class.forName(DynamicHub.java:1408) ... 5 more
这里怎么了?
使用 Java 的动态功能
此异常是由添加 Log4J 库导致的,因为它依赖于反射。native-image
工具执行积极静态分析,以查看应用程序中使用的类。对于未使用的任何类,工具将假定不需要这些类。这称为“封闭式全局”假设 - 生成可执行文件时必须知道需要装入的所有内容。如果静态分析无法找到它,则它不会包含在可执行文件中。
反射是 Java 的核心功能,因此如何使用反射以及利用 GraalVM Native Image 提供的速度提升?您需要一种方法来让 native-image
工具了解反射的任何用法。
幸运的是,native-image
工具能够在配置文件中读取,这些配置文件指定通过反射引用的所有类。
您可以手动执行此操作,或者 GraalVM Java 运行时附带的 Java 跟踪代理可以为您完成此操作。代理生成的 JSON 文件记录应用程序运行时可以找到的所有反射实例、JNI 实例、代理和资源访问。
注意:运行跟踪代理时,务必在应用程序中执行所有代码路径,以确保识别所有的反射情况。
有关跟踪代理的完整文档,请参见此处。
STEP 5:使用跟踪代理
现在,在运行应用程序时,使用跟踪代理生成反射配置。
-
使用跟踪代理运行应用程序:
java -agentlib:native-image-agent=config-output-dir=./src/main/resources/META-INF/native-image -cp ./target/graalvmnidemos-1.0-SNAPSHOT-jar-with-dependencies.jar oracle.App
查看跟踪代理创建的配置文件:
ls -l src/main/resources/META-INF/native-image/
在生成的输出中应看到以下内容:
total 56 -rw-r--r-- 1 kfoster staff 4B Dec 2 19:13 jni-config.json -rw-r--r-- 1 kfoster staff 86B Nov 9 20:46 native-image.properties -rw-r--r-- 1 kfoster staff 65B Dec 2 19:13 predefined-classes-config.json -rw-r--r-- 1 kfoster staff 4B Dec 2 19:13 proxy-config.json -rw-r--r-- 1 kfoster staff 521B Dec 2 19:13 reflect-config.json -rw-r--r-- 1 kfoster staff 101B Dec 2 19:13 resource-config.json -rw-r--r-- 1 kfoster staff 4B Dec 2 19:13 serialization-config.json
注意:项目包含可为您执行此操作的 Maven 配置文件。运行以下命令以使用跟踪代理:
mvn clean package exec:exec -Pjava_agent
-
现在重新生成可执行文件。这次将应用跟踪代理生成的配置文件:
mvn package -Pnative
-
最后,执行生成的文件:
time ./target/file-count
可执行文件可以按预期方式工作并生成输出中的日志消息。
这起作用是因为跟踪代理生成的文件记录了反射引用的类。native-image
工具现在知道它们在应用程序中使用,因此不会将其从生成的可执行文件中排除。
有关 -agentlib
参数的位置的说明
请注意,代理参数必须出现在任何 -jar
或 -classpath
参数之前。还应指定要将文件写入到的目录。建议的位置位于 src/main/resources/META-INF/native-image
下。native-image
工具自动选取放置在此位置的文件。
有关配置生成可执行文件的说明
您还可以使用 Java 属性文件(默认情况下为 src/main/resources/META-INF/native-image/native-image.properties
)将参数传递给 native-image
工具。demo
目录中有一个示例文件,可以让您了解如何处理该文件。
结论
在此实验室中,您试用了一些 GraalVM Native Image 功能:
- 如何从 Java 命令行应用程序生成快速可执行文件
- 如何使用 Maven 构建可执行文件
- 如何使用跟踪代理自动执行跟踪和记录反射过程
使用 GraalVM Native Image 编写高效、安全且可立即扩展的云原生 Java 应用!
了解更多
- 观看本机映像架构师 Christian Wimmer 的演示 GraalVM Native Image:针对 Java 的大规模静态分析
- GraalVM Native Image 参考文档
更多学习资源
在 docs.oracle.com/learn 上浏览其他实验室,或者在 Oracle Learning YouTube 渠道上访问更多免费学习内容。此外,访问 education.oracle.com/learning-explorer 以成为 Oracle Learning Explorer。
有关产品文档,请访问 Oracle 帮助中心。
GraalVM Native Image Quick Start
F51531-04
March 2022
Copyright © 2022, Oracle and/or its affiliates.