实验性代理选项

native-image-agent 工具包含一些目前尚处于实验阶段的选项,这些选项可能会在未来的版本中启用,但也可能被更改或完全删除。这些选项在此处描述。

对预定义类的支持 #

Native-image 要求所有类在镜像构建时都是已知的(一个“封闭世界假设”)。然而,Java 支持在运行时加载新类。为了模拟类加载,可以指示代理跟踪动态加载的类,并将其字节码保存以供镜像构建器稍后使用。通过将 experimental-class-define-support 添加到代理选项字符串中可以启用此功能,例如:-agentlib:native-image-agent=config-output-dir=config,experimental-class-define-support 除了标准配置文件之外,代理还将在配置输出目录中创建一个 agent-extracted-predefined-classes 目录,并即时写入新加载类的字节码。配置目录随后可由镜像构建器直接使用,无需额外调整。这些类将在镜像构建期间加载,但不会被初始化或提供给应用程序。在运行时,如果尝试加载一个与跟踪期间遇到的某个类具有相同名称和字节码的类,则会向应用程序提供该预定义类。

已知限制 #

  • 原生镜像在每次执行中仅支持通过单个类加载器“加载”预定义类一次。
  • 预定义类在运行时“加载”时初始化,不能在构建时初始化。
  • 代理会收集所有不由 Java VM 内置类加载器(有一些例外)加载的类,即来自类路径或模块路径的类。这包括由任何自定义类加载器加载的类。
  • 名称或字节码中包含可变数据(例如序列号、随机数或时间戳)的类通常无法在运行时与预定义类匹配。在这些情况下,需要调整此类类的生成方式。

打印带溯源信息的配置 #

为了调试,了解某些配置条目的来源可能会很有用。通过向代理选项字符串提供 experimental-configuration-with-origins,代理将以树状形式输出配置文件,其中配置条目细分到其源自的调用上下文(堆栈跟踪)。此选项应与 config-output-dir=<path> 结合使用,以告知代理将配置文件输出到何处。代理选项字符串示例:-agentlib:native-image-agent=config-output-dir=config-with-origins/,experimental-configuration-with-origins

从代理输出中省略配置 #

代理可以省略现有配置文件中存在的跟踪配置条目。有两种方法可以指定这些现有配置文件:

  • 通过使用类路径或模块路径中的配置文件。当将 experimental-omit-config-from-classpath 添加到代理选项字符串时,将扫描运行中应用程序的类路径和模块路径,以查找 META-INF/native-image/**/*.json 配置文件。
  • 通过使用 config-to-omit=<path> 明确指定代理到现有配置文件目录。

使用代理生成条件配置 #

代理可以使用启发式方法为用户指定的类生成具有可达性条件的配置。代理将跟踪配置来源并尝试自动推断条件。用户类通过代理过滤文件指定(有关格式的更多信息,请参见有关代理的更多信息)。此外,生成的配置还可以使用另一个过滤文件进行进一步过滤。

目前,此功能支持两种模式:

  1. 在代理的单次运行中生成条件配置。
  2. 从代理的多次运行中生成条件配置,并最终合并所收集的数据。

在代理运行期间生成条件配置 #

要启用此模式,请将 experimental-conditional-config-filter-file=<path> 添加到代理的命令行中,其中 <path> 指向一个代理过滤文件。被此过滤器视为包含的类将被指定为用户代码类。为了进一步过滤生成的配置,您可以使用 conditional-config-class-filter-file=<path>,其中 <path> 是一个代理过滤文件的路径。

从多次代理运行中生成条件配置 #

条件配置可以从到达应用程序中不同代码路径的多次代理运行中生成。每次代理运行都会生成带元数据的配置。然后使用 native-image-configure 合并收集到的数据并生成条件配置。要在此模式下运行代理,请将 experimental-conditional-config-part 添加到代理的命令行中。所有代理运行完成后,您可以通过调用以下命令生成条件配置:

native-image-configure generate-conditional --user-code-filter=<path-to-filter-file> --class-name-filter=<path-to-filter-file> --input-dir=<path-to-agent-run-output-1> --input-dir=<path-to-agent-run-ouput-2> ... --output-dir=<path-to-resulting-conditional-config>

其中:

  • --user-code-filter=<path-to-filter-file>:指向指定用户类的代理过滤文件的路径。
  • (可选)--class-name-filter=<path-to-filter-file>:指向进一步过滤生成配置的代理过滤文件的路径。

底层启发式算法 #

条件是使用应用程序的调用树生成的。启发式算法的工作原理如下:

  1. 对于每个唯一方法,创建调用树中与该方法对应的所有节点的列表。
  2. 对于每个唯一方法,如果该方法在树中有一个以上的调用节点:
    • 在该方法的所有调用节点中查找通用配置。
    • 对于方法的每个调用节点,将这些调用中不常见的配置传播到调用方节点。
  3. 重复第 2 步,直到某个迭代在调用树中没有产生任何变化。
  4. 对于每个包含配置的节点,生成一个以方法类作为条件的条件配置条目。

此启发式算法的主要目标是尝试找出方法根据调用方创建不同配置条目的位置(例如,一个包装 Class.forName 调用的方法)。这意味着该启发式算法不适用于通过不同依赖项生成配置的代码(例如,同一方法根据系统属性使用不同的类参数调用 Class.forName)。

延伸阅读 #

联系我们