Native Image 构建输出

在这里您可以找到关于 GraalVM Native Image 构建输出的信息。下面是构建 HelloWorld 类的原生可执行文件时的示例输出

================================================================================
GraalVM Native Image: Generating 'helloworld' (executable)...
================================================================================
[1/8] Initializing...                                            (2.8s @ 0.15GB)
 Java version: 20+34, vendor version: GraalVM CE 20-dev+34.1
 Graal compiler: optimization level: 2, target machine: x86-64-v3
 C compiler: gcc (linux, x86_64, 12.2.0)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
--------------------------------------------------------------------------------
 Build resources:
 - 13.24GB of memory (42.7% of 31.00GB system memory, determined at start)
 - 16 thread(s) (100.0% of 16 available processor(s), determined at start)
[2/8] Performing analysis...  [****]                             (4.5s @ 0.54GB)
    3,163 reachable types   (72.5% of    4,364 total)
    3,801 reachable fields  (50.3% of    7,553 total)
   15,183 reachable methods (45.5% of   33,405 total)
      957 types,    81 fields, and   480 methods registered for reflection
       57 types,    55 fields, and    52 methods registered for JNI access
        4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                       (0.8s @ 0.99GB)
[4/8] Parsing methods...      [*]                                (0.6s @ 0.75GB)
[5/8] Inlining methods...     [***]                              (0.3s @ 0.32GB)
[6/8] Compiling methods...    [**]                               (3.7s @ 0.60GB)
[7/8] Laying out methods...   [*]                                (0.8s @ 0.83GB)
[8/8] Creating image...       [**]                               (3.1s @ 0.58GB)
   5.32MB (24.22%) for code area:     8,702 compilation units
   7.03MB (32.02%) for image heap:   93,301 objects and 5 resources
   8.96MB (40.83%) for debug info generated in 1.0s
 659.13kB ( 2.93%) for other data
  21.96MB in total
--------------------------------------------------------------------------------
Top 10 origins of code area:            Top 10 object types in image heap:
   4.03MB java.base                        1.14MB byte[] for code metadata
 927.05kB svm.jar (Native Image)         927.31kB java.lang.String
 111.71kB java.logging                   839.68kB byte[] for general heap data
  63.38kB org.graalvm.nativeimage.base   736.91kB java.lang.Class
  47.59kB jdk.proxy1                     713.13kB byte[] for java.lang.String
  35.85kB jdk.proxy3                     272.85kB c.o.s.c.h.DynamicHubCompanion
  27.06kB jdk.internal.vm.ci             250.83kB java.util.HashMap$Node
  23.44kB org.graalvm.sdk                196.52kB java.lang.Object[]
  11.42kB jdk.proxy2                     182.77kB java.lang.String[]
   8.07kB jdk.graal.compiler             154.26kB byte[] for embedded resources
   1.39kB for 2 more packages              1.38MB for 884 more object types
--------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
--------------------------------------------------------------------------------
    0.8s (4.6% of total time) in 35 GCs | Peak RSS: 1.93GB | CPU load: 9.61
--------------------------------------------------------------------------------
Build artifacts:
 /home/janedoe/helloworld/helloworld (executable)
 /home/janedoe/helloworld/helloworld.debug (debug_info)
 /home/janedoe/helloworld/sources (debug_info)
================================================================================
Finished generating 'helloworld' in 17.0s.

构建阶段 #

初始化 #

在此阶段,Native Image 构建过程被设置,并且 Features 被初始化。

Native Image 类型

默认情况下,Native Image 生成可执行文件,但它也可以生成原生共享库静态可执行文件

Java 版本信息

Native Image 进程的 Java 和供应商版本。两者也用于生成的原生二进制文件中的 java.vm.versionjava.vendor.version 属性。当您提交问题时,请报告版本和供应商信息。

Graal 编译器

Graal 编译器使用的选定优化级别和目标机器类型。优化级别可以通过 -O 选项控制,默认为 2,这会启用激进优化。使用 -Ob 启用快速构建模式,这会加快编译阶段。这在开发过程中用于减少镜像构建时间非常有用。使用 -Os 优化大小。目标机器类型可以使用 -march 选项选择,在 AMD64 上默认为 x86-64-v3,在 AArch64 上默认为 armv8-a。有关如何使用此选项的建议,请参见此处

在 Oracle GraalVM 上,该行还会显示关于配置文件引导优化 (PGO) 的信息。

  • off: 未使用 PGO
  • instrument: 生成的可执行文件或共享库被插桩以收集 PGO 数据 (--pgo-instrument)
  • user-provided: PGO 已启用并使用用户提供的配置文件(例如 --pgo default.iprof
  • ML-inferred: 使用机器学习 (ML) 模型来静态推断控制分割分支的配置文件。

C 编译器

Native Image 构建过程使用的 C 编译器可执行文件、供应商、目标架构和版本信息。

垃圾收集器

生成的本机可执行文件中使用的垃圾收集器

  • Serial GC 是默认的 GC,为低内存占用和小型 Java 堆大小进行了优化。
  • G1 GC(GraalVM Community Edition 中不提供)是多线程 GC,旨在减少“stop-the-world”暂停,从而在实现高吞吐量的同时提高延迟。
  • Epsilon GC 不执行任何垃圾收集,专为运行时间极短且仅分配少量内存的应用程序而设计。

更多信息请参见内存管理文档

最大堆大小

默认情况下,堆大小限制为系统内存的特定百分比,允许垃圾收集器根据其策略自由分配内存。在调用原生可执行文件时使用 -Xmx 选项(例如 ./myapp -Xmx64m 代表 64MB)来限制最大堆大小,以实现更低且更可预测的内存占用。这在某些情况下还可以改善延迟。在使用 Native Image 构建时使用 -R:MaxHeapSize 选项来预配置最大堆大小。

用户特定功能

所有由用户提供或特别启用,或由框架隐式注册的Features。GraalVM Native Image 部署了许多内部功能,这些功能不在此列表中。

实验性选项

所有活动实验性选项的列表,包括它们的来源以及可能存在的 API 选项替代方案(如果可用)。

在生产环境中应避免使用实验性选项,它们可能在任何版本中发生变化。如果您依赖实验性功能并希望某个选项被视为稳定,请提交问题。

已启用的 NATIVE_IMAGE_OPTIONS

通过 NATIVE_IMAGE_OPTIONS 环境变量获取的额外构建选项。与 JAVA_TOOL_OPTIONS 类似,环境变量的值会添加到提供给 native-image 的选项前面。不允许通过 NATIVE_IMAGE_OPTIONS 传递参数文件。NATIVE_IMAGE_OPTIONS 环境变量旨在供用户、构建环境或工具注入额外构建选项。

构建资源

构建过程使用的内存限制和线程数。

更精确地说,是 Java 堆的内存限制,因此实际内存消耗可能更高。请查看构建结束时报告的峰值 RSS 以了解实际使用了多少内存。默认情况下,构建过程尝试仅使用空闲内存(以避免构建机器上的内存压力),且不超过 32GB 内存。如果空闲内存少于 8GB,构建过程将退回到使用总内存的 85%。因此,如果您的机器在构建过程中变慢,请考虑释放内存,例如通过关闭不需要的应用程序。可以通过覆盖默认行为,例如使用 -J-XX:MaxRAMPercentage=60.0-J-Xmx16g

默认情况下,构建过程使用所有可用处理器来最大限度地提高速度,但线程数不超过 32。使用 --parallelism 选项可以显式设置线程数(例如,--parallelism=4)。使用更少的线程可以减少系统负载和内存消耗(代价是构建过程会变慢)。

执行分析 #

在此阶段,执行指向分析。进度指示器显示分析迭代的次数。大量的迭代可能表明分析存在问题,这很可能是由于配置错误或功能异常导致的。

可达类型、字段和方法

可达类型(原始类型、类、接口和数组)、字段和方法的数量与作为构建过程一部分加载的总类型、字段和方法的数量之比。大量加载的元素不可达可能表明存在配置问题。为减少开销,请确保您的类路径和模块路径只包含构建应用程序所需的条目。

反射注册

已注册用于反射的类型、字段和方法的数量。大量注册可能导致显著的反射开销,减慢构建过程,并增加原生二进制文件的大小(请参阅反射元数据)。

JNI 访问注册

已注册用于 JNI 访问的类型、字段和方法的数量。

外部函数存根

外部函数访问注册的 downcall 和 upcall 数量。

运行时编译方法

标记为运行时编译的方法数量。此数字仅在可执行文件中内置运行时编译时显示,例如在构建 Truffle 语言时。运行时编译方法占堆中的图编码

构建宇宙 #

在此阶段,会构建一个包含所有类型、字段和方法的宇宙,然后将其用于创建原生二进制文件。

解析方法 #

在此阶段,Graal 编译器解析所有可达方法。进度指示器将以递增的间隔定期打印。

内联方法 #

在此阶段,执行琐碎方法内联。进度指示器显示内联迭代的次数。

编译方法 #

在此阶段,Graal 编译器将所有可达方法编译为机器码。进度指示器将以递增的间隔定期打印。

布局方法 #

在此阶段,编译后的方法被布局。进度指示器将以递增的间隔定期打印。

创建镜像 #

在此阶段,原生二进制文件被创建并写入磁盘。调试信息也在此阶段生成(如果请求)。

代码区域

代码区域包含 Graal 编译器为所有可达方法生成的机器代码。因此,减少可达方法的数量也会减少代码区域的大小。

代码区域来源

为帮助用户了解代码区域的机器代码来源,构建输出显示了主要来源的细分。来源是 Java 源文件组,可以是 JAR 文件、包名或类名,具体取决于可用信息。java.base 模块例如包含 JDK 的基础类。svm.jar 文件、org.graalvm.nativeimage.base 模块以及类似的来源包含 Native Image 运行时的内部源文件。为了减小代码区域的大小,进而减小原生可执行文件的总大小,请根据代码区域细分重新评估应用程序的依赖项。某些库和框架比其他更适合 Native Image,而库或框架的更新版本可能会改善(或恶化)其代码占用空间。

镜像堆

堆包含可达对象,例如静态应用程序数据、元数据和用于不同目的的 byte[](见下文)。

存储在 byte[] 中的通用堆数据

所有 byte[] 对象的总大小,这些对象既不用于 java.lang.String,也不用于代码元数据,也不用于反射元数据,也不用于图编码。因此,这也可以包括来自应用程序代码的 byte[] 对象。

存储在 byte[] 中的嵌入资源

所有用于在原生二进制文件中存储资源(例如,通过 Class.getResource() 访问的文件)的 byte[] 对象的总大小。资源的数量显示在部分。包含所有资源(包括其模块、名称、来源和大小等附加信息)的列表包含在构建报告中。此信息也可以使用 -H:+GenerateEmbeddedResourcesFile 选项以 JSON 格式请求。这样的 JSON 文件根据embedded-resources-schema-v1.0.0.json 中定义的 JSON 模式进行验证。

存储在 byte[] 中的代码元数据

用于代码区域元数据的所有 byte[] 对象的总大小。因此,减少可达方法的数量也会减少此元数据的大小。

存储在 byte[] 中的反射元数据

所有用于反射元数据(包括类型、字段、方法和构造函数数据)的 byte[] 对象的总大小。要减少反射元数据的数量,请减少注册用于反射的元素的数量。

存储在 byte[] 中的图编码

所有用于图编码的 byte[] 对象的总大小。这些编码是运行时编译方法的结果。因此,减少此类方法的数量也会减少相应图编码的大小。

堆对齐

选定的垃圾收集器对齐堆而保留的额外空间。堆对齐也可能包含特定于 GC 的数据结构。因此,其大小只能通过切换到不同的垃圾收集器来影响。

调试信息

生成的调试信息的总大小(如果启用)。

其他数据

二进制文件中既不在代码区域、也不在、也不在调试信息中的数据量。这些数据通常包含 Native Image 的内部信息,不应占据主导地位。

安全报告 #

此部分在 GraalVM Community Edition 中不可用。

反序列化

这显示了 Java 反序列化是否包含在原生可执行文件中。如果未包含,则可执行文件的攻击面会减小,因为可执行文件不能被基于 Java 反序列化的攻击利用。

软件物料清单 (SBOM)

本节指示是否组装了 SBOM 以及它以何种方式存储。存储格式包括:embed,将 SBOM 嵌入到二进制文件中;classpath,将 SBOM 保存到类路径中;以及 export,将 SBOM 作为 JSON 构建产物包含。使用 --enable-sbom 激活此功能,该功能默认为 embed 选项。嵌入时,会显示 SBOM 大小。组件数量始终显示。

有关更多信息,请参阅软件物料清单

反向边缘控制流完整性 (CFI)

控制流完整性 (CFI) 可以通过实验性的 -H:CFI=HW 选项强制执行。此功能目前仅适用于 Graal 为 Linux AArch64 编译的代码,并利用指针认证码 (PAC) 来确保函数返回地址的完整性。

软件控制流完整性 (CFI)

控制流完整性 (CFI) 可以通过实验性的 -H:CFI=SW_NONATIVE 选项在软件中强制执行。此功能目前仅适用于 Graal 为 Linux AMD64 编译的代码,并验证间接分支和方法返回的目标。

建议 #

构建输出可能包含以下一项或多项建议,这些建议可帮助您充分利用 Native Image。

AWT: 抽象窗口工具包缺少可达性元数据

Native Image 分析已包含来自java.awt的类,但找不到任何可达性元数据。使用跟踪代理为您的应用程序收集此类元数据。否则,您的应用程序可能无法正常工作。如果您的应用程序不是桌面应用程序(例如直接使用 Swing 或 AWT),您可能需要重新评估是否确实需要 AWT 依赖项。

HOME: 运行二进制文件时设置 java.home

Native Image 分析已检测到 System.getProperty("java.home") 的使用。为确保其返回有效值,请通过向二进制文件传递 -Djava.home=<path> 选项来设置 java.home。如果未设置,System.getProperty("java.home") 将返回 null

CPU: 启用更多 CPU 功能以提高性能

Native Image 构建过程已确定您的 CPU 支持比当前启用的更多功能,例如 AES 或 LSE。如果您在支持相同 CPU 功能的同一台机器或类似机器上部署应用程序,请考虑在构建时使用 -march=native。此选项允许 Graal 编译器使用所有可用的 CPU 功能,这反过来可以显著提高应用程序的性能。使用 -march=list 列出所有可以显式指定的目标机器类型。

G1GC: 使用 G1 垃圾收集器以提高延迟和吞吐量

G1 垃圾收集器适用于您的平台。考虑在构建时使用 --gc=G1 启用它,以提高应用程序的延迟和吞吐量。更多信息请参见内存管理文档。为了获得最佳峰值性能,还请考虑使用配置文件引导优化

HEAP: 指定最大堆大小

请参阅最大堆大小

PGO: 使用配置文件引导优化以提高吞吐量

考虑使用配置文件引导优化 (PGO) 来优化您的应用程序以提高吞吐量。这些优化允许 Graal 编译器在 AOT 编译您的应用程序时利用分析信息,类似于它作为 JIT 编译器运行时。为此,请执行以下步骤

  1. 使用 --pgo-instrument 构建您的应用程序。
  2. 使用代表性工作负载运行您的插桩应用程序,以生成 .iprof 文件形式的分析信息。
  3. 重新构建您的应用程序,并使用 --pgo=<your>.iprof 传入分析信息,以生成应用程序的优化版本。

相关指南:使用配置文件引导优化优化原生可执行文件

为了获得最佳峰值性能,还请考虑使用G1 垃圾收集器

QBM: 使用快速构建模式以加快构建速度

考虑使用快速构建模式 (-Ob) 来加快开发期间的构建速度。更准确地说,此模式减少了 Graal 编译器执行的优化次数,从而缩短了编译阶段的总时间。快速构建模式不仅对开发有用,它还可以使生成的可执行文件尺寸更小。但请注意,由于优化次数减少,可执行文件的整体峰值吞吐量可能会降低。

INIT: 使用严格镜像堆配置

开始使用 --strict-image-heap 以减少配置量,并为未来的 GraalVM 版本做准备,这将成为默认设置。此模式要求只有存储在镜像堆中的类才需要用 --initialize-at-build-time 标记。这有效地减少了实现构建时初始化所需的配置条目数量。采用新模式时,最好从头开始引入构建时初始化。在此过程中,最好选择单个类(而不是整个包)进行构建时初始化。此外,在迁移到新标志之前,请务必将所有框架依赖项更新到最新版本,因为它们可能也需要迁移。

请注意,从 GraalVM for JDK 22 开始,Native Image 中默认启用 --strict-image-heap

资源使用统计 #

垃圾收集

所有垃圾收集器花费的总时间、总 GC 时间占总进程时间的百分比以及垃圾收集的总次数。大量的收集或收集器中花费的时间通常表示系统处于内存压力之下。增加可用内存量以减少构建原生二进制文件所需的时间。

峰值 RSS

操作系统报告的峰值常驻集大小。此值表示构建过程消耗的最大内存量。您可能希望将此值与构建资源部分报告的内存限制进行比较。如果内存充足且GC 统计未显示任何问题,则可以将系统的总内存量减少到接近峰值 RSS 的值,以降低运营成本。

CPU 负载

进程使用的 CPU 时间除以总进程时间。增加 CPU 核心数以减少构建原生二进制文件所需的时间。

构建产物 #

所有构建产物的列表。这包括生成的原生二进制文件,但也可以包含其他产物,例如附加库、C 头文件或调试信息。其中一些产物必须与原生二进制文件保持在相同位置,因为它们在运行时需要。例如,对于使用 AWT 的应用程序,构建过程还会输出 JDK 中的库和垫片,以提供兼容的 AWT 支持。这些库需要与原生二进制文件一起复制和分发。使用 -H:+GenerateBuildArtifactsFile 选项指示构建器以 JSON 格式生成机器可读的构建产物列表。这样的 JSON 文件根据build-artifacts-schema-v0.9.0.json 中定义的 JSON 模式进行验证。此模式还包含每种可能产物类型的描述,并解释它们在运行时是否需要。

机器可读的构建输出 #

native-image 构建器生成的构建输出是为人设计的,会随新版本演进,因此不应以任何方式由工具解析。相反,请使用 -H:BuildOutputJSONFile=<file.json> 选项指示构建器以 JSON 格式生成机器可读的构建输出,该输出可用于例如构建监控工具。这样的 JSON 文件根据build-output-schema-v0.9.3.json 中定义的 JSON 模式进行验证。请注意,仅当构建成功时才会生成 JSON 文件。

以下示例说明了如何在 CI/CD 构建管道中使用此功能来检查可达方法的数量是否未超过某个阈值

native-image -H:BuildOutputJSONFile=build.json HelloWorld
# ...
cat build.json | python3 -c "import json,sys;c = json.load(sys.stdin)['analysis_results']['methods']['reachable']; assert c < 12000, f'Too many reachable methods: {c}'"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError: Too many reachable methods: 12128

彩色构建输出 #

默认情况下,当 native-image 构建器找到合适的终端时,它会为构建输出着色以提高可读性。在检查颜色支持时,它还会遵守 NO_COLORCITERM 环境变量。要明确控制彩色输出,请将 --color 选项设置为 alwaysneverauto(默认)。

联系我们