- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
使用追踪代理收集元数据
Native Image 工具依赖于对应用程序运行时可达代码的静态分析。然而,这种分析并非总能完全预测 Java Native Interface (JNI)、Java 反射、动态代理对象或类路径资源的所有用法。这些动态特性中未被检测到的用法必须以元数据(在代码中预先计算或作为 JSON 配置文件)的形式提供给 native-image
工具。
在这里,您将找到关于如何自动收集应用程序元数据并编写 JSON 配置文件的信息。要了解如何在代码中计算动态特性调用,请参阅可达性元数据。
目录 #
跟踪代理 #
GraalVM 提供了一个跟踪代理,用于轻松收集元数据并准备配置文件。该代理在常规 Java VM 上执行应用程序期间跟踪动态特性的所有用法。
使用 GraalVM JDK 中的 java
命令在命令行启用代理
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/ ...
注意:
-agentlib
必须在-jar
选项或类名或任何应用程序参数之前指定,作为java
命令的一部分。
运行时,代理会查找 native-image
工具需要额外信息的类、方法、字段和资源。当应用程序完成且 JVM 退出时,代理会将元数据写入指定输出目录 (/path/to/config-dir/
) 中的 JSON 文件。
为了更好地覆盖动态特性,可能需要多次运行应用程序(使用不同的执行路径)。config-merge-dir
选项会将配置添加到现有配置集合中,如下所示
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-merge-dir=/path/to/config-dir/ ... ^^^^^
代理还提供以下选项以定期写入元数据
config-write-period-secs=n
:每n
秒写入元数据文件;n
必须大于 0。config-write-initial-delay-secs=n
:首次写入元数据前等待n
秒;默认为1
。
例如
$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/,config-write-period-secs=300,config-write-initial-delay-secs=5 ...
上述命令将在初始延迟 5 秒后,每 300 秒将元数据文件写入 /path/to/config-dir/
。
建议手动检查生成的配置文件。由于代理仅观察已执行的代码,因此应用程序输入应尽可能覆盖更多的代码路径。
生成的配置文件可以通过将其放置在类路径上的 META-INF/native-image/
目录中来提供给 native-image
工具。此目录(或其任何子目录)会搜索名为 reachability-metadata.json 的文件,然后该文件会自动包含在构建过程中。并非所有这些文件都必须存在。当找到多个同名文件时,所有文件都会被考虑。
要测试代理在示例应用程序上收集元数据,请参阅使用反射构建原生可执行文件指南。
条件元数据收集 #
代理可以根据代码中的使用情况推断元数据条件。条件元数据主要面向库维护者,目标是减少整体占用空间。
要使用代理收集条件元数据,请参阅条件元数据收集。
代理高级用法 #
基于调用者的过滤器 #
默认情况下,代理会过滤掉 Native Image 无需配置即可支持的动态访问。过滤机制通过识别执行访问的 Java 方法(也称为调用者方法),并将其声明类与一系列过滤规则进行匹配来工作。内置过滤规则从生成的配置文件中排除了源自 JVM 或 Java 类库中 Native Image 直接支持的部分(例如 java.nio
)的动态访问。被访问的项(类、方法、字段、资源等)与过滤无关。
除了内置过滤器外,还可以使用 caller-filter-file
选项指定包含附加规则的自定义过滤器文件。例如:-agentlib:caller-filter-file=/path/to/filter-file,config-output-dir=...
过滤器文件具有以下结构
{ "rules": [
{"excludeClasses": "com.oracle.svm.**"},
{"includeClasses": "com.oracle.svm.tutorial.*"},
{"excludeClasses": "com.oracle.svm.tutorial.HostedHelper"}
],
"regexRules": [
{"includeClasses": ".*"},
{"excludeClasses": ".*\\$\\$Generated[0-9]+"}
]
}
rules
部分包含一系列规则。每个规则指定 includeClasses
(表示源自匹配类的查找将包含在生成的配置中)或 excludeClasses
(表示源自匹配类的查找将从配置中排除)。每个规则定义一个用于匹配类的模式。模式可以以 .*
或 .**
结尾,解释如下:- .*
匹配包中的所有类,且仅匹配该包;- .**
匹配包中的所有类以及所有子包(任意深度)。如果没有 .*
或 .**
,则该规则仅适用于具有与模式匹配的完全限定名称的单个类。所有规则都按照它们指定的顺序进行处理,因此后面的规则可以部分或完全覆盖前面的规则。当提供多个过滤器文件时(通过指定多个 caller-filter-file
选项),它们的规则按照文件指定的顺序链接在一起。内置调用者过滤器的规则总是首先处理,因此它们可以在自定义过滤器文件中被覆盖。
在上面的例子中,第一条规则将所有源自 com.oracle.svm
包及其所有子包(及其子包等)中的类的查找从生成的元数据中排除。然而,在下一条规则中,直接位于 com.oracle.svm.tutorial
包中的类的查找又被包含进来。最后,来自 HostedHelper
类的查找又被排除。这些规则中的每一个都部分覆盖了之前的规则。例如,如果规则的顺序相反,那么 com.oracle.svm.**
的排除将是最后一条规则,并会覆盖所有其他规则。
regexRules
部分也包含一系列规则。其结构与 rules
部分相同,但规则被指定为正则表达式模式,用于匹配整个完全限定的类标识符。regexRules
部分是可选的。如果指定了 regexRules
部分,则当且仅当 rules
和 regexRules
都包含该类且两者均不排除它时,该类才被视为包含在内。如果没有 regexRules
部分,则只有 rules
部分决定是否包含或排除类。
出于测试目的,可以通过添加 no-builtin-caller-filter
选项来禁用 Java 类库查找的内置过滤器,但生成的元数据文件通常不适用于构建。类似地,基于启发式的 Java VM 内部访问的内置过滤器可以使用 no-builtin-heuristic-filter
禁用,这通常也会导致元数据文件可用性降低。例如:-agentlib:native-image-agent=no-builtin-caller-filter,no-builtin-heuristic-filter,config-output-dir=...
访问过滤器 #
与上述基于调用者的过滤器(根据动态访问的来源进行过滤)不同,访问过滤器适用于访问的目标。因此,访问过滤器可以直接从生成的配置中排除包和类(及其成员)。
默认情况下,所有被访问的类(也通过了基于调用者的过滤器和内置过滤器)都包含在生成的配置中。使用 access-filter-file
选项,可以添加一个遵循上述文件结构的自定义过滤器文件。该选项可以多次指定以添加多个过滤器文件,并且可以与其他过滤器选项结合使用,例如:-agentlib:access-filter-file=/path/to/access-filter-file,caller-filter-file=/path/to/caller-filter-file,config-output-dir=...
。
将配置文件指定为参数 #
可以通过 -H:ConfigurationFileDirectories=/path/to/config-dir/
将包含配置文件但不在类路径中的目录指定给 native-image
。此目录必须直接包含 reachability-metadata.json 或以前使用的单个元数据文件(jni-config.json、reflect-config.json、proxy-config.json、serialization-config.json 和 resource-config.json)。类路径上但在 META-INF/native-image/
之外包含相同元数据文件的目录可以通过 -H:ConfigurationResourceRoots=path/to/resources/
提供。-H:ConfigurationFileDirectories
和 -H:ConfigurationResourceRoots
都可以接受逗号分隔的目录列表。
通过进程环境注入代理 #
如果 Java 进程是由应用程序或脚本文件启动,或者 Java 甚至嵌入在现有进程中,那么修改 java
命令行来注入代理可能会很困难。在这种情况下,也可以通过 JAVA_TOOL_OPTIONS
环境变量来注入代理。此环境变量可以被同时运行的多个 Java 进程拾取,在这种情况下,每个代理必须使用 config-output-dir
写入独立的输出目录。(下一节描述如何合并配置文件的集合。)为了在单个全局 JAVA_TOOL_OPTIONS
变量中使用不同的路径,代理的输出路径选项支持占位符
export JAVA_TOOL_OPTIONS="-agentlib:native-image-agent=config-output-dir=/path/to/config-output-dir-{pid}-{datetime}/"
{pid}
占位符被替换为进程标识符,而 {datetime}
则被替换为 UTC 格式(符合 ISO 8601)的系统日期和时间。对于上述示例,结果路径可能是:/path/to/config-output-dir-31415-20181231T235950Z/
。
跟踪文件 #
在上面的例子中,native-image-agent
既用于跟踪 JVM 上的动态访问,又用于从中生成一组配置文件。然而,为了更好地理解执行情况,代理还可以以 JSON 格式写入一个包含每个独立访问的跟踪文件
$JAVA_HOME/bin/java -agentlib:native-image-agent=trace-output=/path/to/trace-file.json ...
native-image-configure
工具可以将跟踪文件转换为配置文件。以下命令读取并处理 trace-file.json
并在目录 /path/to/config-dir/
中生成一组配置文件
native-image-configure generate --trace-input=/path/to/trace-file.json --output-dir=/path/to/config-dir/
互操作性 #
代理使用 JVM Tool Interface (JVMTI),并且可以与支持 JVMTI 的其他 JVM 配合使用。在这种情况下,需要提供代理的绝对路径
/path/to/some/java -agentpath:/path/to/graalvm/jre/lib/amd64/libnative-image-agent.so=<options> ...
实验性选项 #
代理有一些目前处于实验阶段的选项,它们可能在未来版本中启用,但也可能被更改或完全删除。请参阅ExperimentalAgentOptions.md指南。
Native Image 配置工具 #
如上一节所述,当同时在多个进程中使用代理时,config-output-dir
是一个安全的选项,但这会导致生成多组配置文件。native-image-configure
工具可用于合并这些配置文件
native-image-configure generate --input-dir=/path/to/config-dir-0/ --input-dir=/path/to/config-dir-1/ --output-dir=/path/to/merged-config-dir/
此命令从 /path/to/config-dir-0/
读取一组配置文件,从 /path/to/config-dir-1/
读取另一组,然后将包含它们两者信息的一组配置文件写入 /path/to/merged-config-dir/
。可以指定任意数量的 --input-dir
参数,每个参数带有一组配置文件。有关所有选项,请参阅 native-image-configure help
。