JEP 514:提前编译命令行的易用性优化
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选项。