JEP 514:提前编译命令行的易用性优化

原文:JEP 514- Ahead-of-Time Command-Line Ergonomics
作者:
日期:2025-10-28

负责人约翰·罗斯(John Rose)
类型特性
范围JDK
状态已完成 / 已交付
版本25
组件HotSpot / 运行时
讨论地址leyden - dev@openjdk.org
工作量M
持续时间S
相关内容JEP 483:提前编译类加载与链接
审核人亚历克斯·巴克利(Alex Buckley)、弗拉基米尔·科兹洛夫(Vladimir Kozlov)
批准人弗拉基米尔·科兹洛夫(Vladimir Kozlov)
创建时间2025/02/13 18:07
更新时间2025/10/21 23:40
问题编号8350022

摘要

通过简化常见用例所需的命令,让创建 提前编译缓存 变得更加容易,从而加快 Java 应用程序的启动速度。

目标

  • 在不损失表达能力的前提下,简化创建提前编译(AOT)缓存的过程。
  • 不引入全新的 AOT 工作流程,而是让访问现有工作流程变得更加便捷。

非目标

  • 本特性的目标并非引入新的 AOT 优化;而是旨在简化对所有现有及未来 AOT 优化的访问。

动机

JEP 483 引入的提前编译缓存可加快 Java 应用程序的启动速度。随着 莱登项目 为 HotSpot JVM 带来新的与 AOT 相关的优化,其优势预计将进一步提升。

在 JDK 24 中,创建 AOT 缓存需分两步进行,需在两种不同的 _AOT 模式 _ 下调用 java 启动器。第一次调用指定 record 模式,指示 JVM 观察应用程序的 _ 训练运行 _ 动态,并将其记录到一个 _AOT 配置 _ 中。第二次调用指定 create 模式,指示 JVM 根据训练运行期间记录的配置创建 AOT 缓存。

以下是这个两步工作流程的示例,取自 JEP 483

$ java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf \
       -cp app.jar com.example.App ...
$ java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf \
       -XX:AOTCache=app.aot
  

随后,只需指定 AOT 缓存即可运行应用程序:

$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
  

应用程序的这次 _ 生产运行 _ 启动得更快,因为无需发现、加载和链接应用程序的类。这些类可直接从缓存中获取。

为了创建 AOT 缓存而必须运行两次 java 很不方便。而且 AOT 配置文件会残留下来,这也很麻烦 —— 它只是一个临时文件,生产运行时并不需要,可以删除。

至少在常见用例中,如果只需一步就能完成训练运行并创建 AOT 缓存,那就方便得多了。这对用户来说很高效,对于像 JRuby 这样针对自定义工作负载协调自身训练的应用程序来说也很便利。对于不常见的用例,仍可保留显式指定 AOT 模式和 AOT 配置的功能。

描述

我们为 java 启动器扩展了一个新的命令行选项 AOTCacheOutput,用于指定 AOT 缓存输出文件。当单独使用此选项且不使用其他 AOT 选项时,该选项实际上会使启动器将其调用拆分为两个子调用:第一个进行训练运行(AOTMode=record),然后第二个创建 AOT 缓存(AOTMode=create)。

例如,前面展示的两步工作流程可以用单一步骤替代:

$ java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...
  

为了方便起见,以这种方式操作时,JVM 会为 AOT 配置创建一个临时文件,并在完成后删除该文件。

使用 AOT 缓存的生产运行与以前的启动方式相同:

$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
  

一个新的环境变量 JDK_AOT_VM_OPTIONS 可用于传递专门应用于执行缓存创建的子调用的命令行选项,而不会影响执行训练运行的子调用。其语法与现有的 JAVA_TOOL_OPTIONS 环境变量相同。这使得即使在因命令行选项不同而看似需要两步工作流程的用例中,单步工作流程也能适用。

这些选项的完整规范可在 此处 获取。

手动协调训练和缓存创建

仍有一些用例可能更适合使用两步工作流程,每次都显式指定 AOT 模式(先 AOTMode=create,然后 AOTMode=record)。

例如,如果你打算将应用程序部署到云中的小型实例上,那么你可以在小型实例上进行训练运行,但在大型实例上创建 AOT 缓存。这样,训练运行反映了部署环境,而 AOT 缓存的创建可以利用大型实例额外的 CPU 核心和内存。

随着莱登 AOT 优化变得更加复杂,这种分工可能会变得更加重要。例如,未来的某些 AOT 优化在小型实例上创建缓存可能需要几分钟时间,但在大型实例上可能只需要几秒钟。

此外,单步工作流程在资源受限的环境中可能无法按预期运行。创建 AOT 缓存的子调用使用其自身的 Java 堆,大小与训练运行使用的堆相同。因此,完成单步工作流程所需的内存是命令行指定堆大小的两倍。例如,如果单步工作流程 java -XX:AOTCacheOutput=... 伴随着 -Xms4g -Xmx4g,指定了一个 4GB 的堆,那么环境需要 8GB 内存才能完成该工作流程。在资源受限环境中的用户,如果单步工作流程无法成功完成,应使用两步工作流程。

替代方案

  • 我们考虑过一种组合的 AOT 模式,即 AOTMode=record+create。这种方式目前可行,但未来我们可能会扩展训练运行以读取现有的 AOT 缓存。到那时,-XX:AOTCache=myapp.aot 这个选项就会变得模糊不清,而且无论如何我们可能最终还是会引入 AOTCacheOutput 选项。