JDK-8350022 : JEP 514: Ahead-of-Time Command-Line Ergonomics
  • Type: JEP
  • Component: hotspot
  • Sub-Component: runtime
  • Priority: P4
  • Status: Targeted
  • Resolution: Unresolved
  • Fix Versions: 25
  • Submitted: 2025-02-13
  • Updated: 2025-05-22
Related Reports
Blocks :  
Relates :  
Relates :  
Sub Tasks
JDK-8355798 :  
Description
Summary
-------

Make it easier to create [ahead-of-time caches][JEP 483], which accelerate the startup of Java applications, by simplifying the commands required for common use cases.

[JEP 483]: https://openjdk.org/jeps/483


Goals
-----

  - Simplify the process of creating an ahead-of-time (AOT) cache, with no loss of expressiveness.

  - Do not introduce fundamentally new AOT workflows, but rather make it easier to access existing ones.


## Non-Goals

  - It is not a goal to introduce new AOT optimizations; rather, we aim to simplify access to all AOT optimizations, both present and future.


## Motivation

Ahead-of-time caches, introduced by [JEP 483], accelerate the startup of Java applications. Their benefits are expected to grow as [Project Leyden](https://openjdk.org/projects/leyden) brings new AOT-related optimizations to the HotSpot JVM.

With JDK 24, you create an AOT cache in two steps, invoking the `java` launcher in two distinct _AOT modes_. The first invocation specifies `record` mode, directing the JVM to observe the dynamics of a _training run_ of your application and record them into an _AOT configuration_. The second invocation specifies `create` mode, directing the JVM to create an AOT cache based on the configuration recorded during the training run.

Here is an example of this two-step workflow, taken from [JEP 483][483-desc]:

<pre><code>$ java <b>-XX:AOTMode=record -XX:AOTConfiguration=app.aotconf</b> \
       -cp app.jar com.example.App ...

$ java <b>-XX:AOTMode=create -XX:AOTConfiguration=app.aotconf</b> \
       <b>-XX:AOTCache=app.aot</b>
</code></pre>

Subsequently, you run your application specifying just the AOT cache:

<pre><code>$ java <b>-XX:AOTCache=app.aot</b> -cp app.jar com.example.App ...
</code></pre>

This _production run_ of the application starts more quickly because the application's classes do not need to be discovered, loaded, and linked. They are available instantly from the cache.

It is inconvenient to have to run `java` twice in order to create an AOT cache. It is also inconvenient to have the AOT configuration file left over — it is just a temporary file, not required for production runs, and can be deleted.

It would be far more convenient if, at least in common use cases, only one step were needed to perform a training run and create an AOT cache. This would be efficient for users and also convenient for applications such as [JRuby] that coordinate their own training against custom workloads. The ability to specify AOT modes and AOT configurations explicitly can remain for uncommon use cases.

[483-desc]: https://openjdk.org/jeps/483#Description
[JRuby]: https://blog.headius.com/2025/02/boosting-jruby-startup-with-appcds-and-aotcache


## Description

We extend the `java` launcher with a new command-line option, `AOTCacheOutput`, that specifies an AOT cache output file. When used alone, with no other AOT options, this option causes the launcher to, in effect, split its invocation into two sub-invocations: The first does a training run (`AOTMode=record`) and then the second creates the AOT cache (`AOTMode=create`).

For example, the two-step workflow shown earlier can be replaced by the single step:

<pre><code>$ java <b>-XX:AOTCacheOutput=app.aot</b> -cp app.jar com.example.App ...
</code></pre>

As a convenience, when operating in this way the JVM creates a temporary file for the AOT configuration and deletes the file when finished.

A production run that uses the AOT cache is started the same way as before:

<pre><code>$ java <b>-XX:AOTCache=app.aot</b> -cp app.jar com.example.App ...
</code></pre>

A new environment variable, `JDK_AOT_VM_OPTIONS`, can be used to pass command-line options that apply specifically to cache creation (`AOTMode=create`), without affecting the training run (`AOTMode=record`). The syntax is the same as for the existing [`JAVA_TOOL_OPTIONS`] environment variable. This enables the one-step workflow to apply even in use cases where it might seem that two steps are necessary due to differences in the command-line options.

[`JAVA_TOOL_OPTIONS`]: https://docs.oracle.com/en/java/javase/24/docs/specs/jvmti.html#tooloptions

The full specification of these options is available [here](https://bugs.openjdk.org/browse/JDK-8356010).


### Manually orchestrating training and cache creation

There are still use cases where it may be preferable to use two steps to create an AOT cache, specifying the AOT mode explicitly each time.

For example, if you intend to deploy an application to small instances in a cloud then you could do the training run on a small instance but create the AOT cache on a large instance. That way the training run reflects the deployment environment, but the creation of the AOT cache can leverage the additional CPU cores and memory of the large instance.

Such a division of labor may become more important as Leyden AOT optimizations become more complex. For example, some future AOT optimization might require minutes of time to create a cache on a small instance, but just seconds on a large instance.


## Alternatives

  - We considered a combined AOT mode, `AOTMode=record+create`. That could work today, but in the future we may extend training runs to read an existing AOT cache. At that point, the option `-XX:AOTCache=myapp.aot` would become ambiguous, and we would likely wind up introducing the `AOTCacheOutput` option anyway.