JEP draft: Ahead-of-time Command Line Ergonomics

OwnerJohn Rose
TypeFeature
ScopeJDK
StatusDraft
Componenthotspot / runtime
EffortM
DurationS
Created2025/02/13 18:07
Updated2025/02/22 03:23
Issue8350022

Summary

Make it easier to create an ahead-of-time cache (as defined by JEP 483) for a Java application, by simplifying the commands required by some common use cases.

Goals

Non-Goals

Motivation

Java application start-up is accelerated by use of an ahead-of-time cache, as defined by JEP 483, and the benefits are expected to grow as new AOT-related optimizations are added to the VM.

Currently, the user creates an AOT cache by means of two commands. These two commands specify distinct AOT modes using the option AOTMode=<mode>. The first command specifies AOTMode=record, requesting the VM to observe and record of the dynamics of a training run of the user's Java application. The second command specifies AOTMode=create, which requests creation of an actual AOT cache, whose name is specified by another command line option such as -XX:AOTCache=myapp.aot; this option names the file which will store the AOT cache.

The accelerated application will then be deployed into production by a third command which specifies the same AOT cache, using an option like -XX:AOTCache=myapp.aot, again mentioning the AOT cache file. This command instructs the VM to make a production run from the existing AOT cache. The application can be executed in production as many times as desired, since the AOT cache is indefinitely reusable. Production runs also have an AOT mode (AOTMode=auto), but it is the default setting of the java command and so does not need to be mentioned explicitly on the command line. The production run only needs the option that selects the AOT cache file, -XX:AOTCache=myapp.aot.

The first two commands (with AOTMode=record and AOTMode=create) work together, splitting responsibility for creating the AOT cache file. The first command makes the necessary training run and emits its observations in a temporary file (an AOT configuration), while the second command consults that information in order to assemble the AOT cache. The AOT configuration file used by those two commands to communicate is an AOT cache file, but rather a special file, of an undocumented format and limited usefulness. The name of this file must be chosen by the user using a command line option such as -XX:AOTConfiguration=app.aotconf, and that same command line option must be supplied to both cooperating commands. Note that the second command does not re-run the user's application; it merely takes the configuration file and assembles the requested AOT cache. This second command line is usually a simple reformulation of the first command line. The user is responsible for deleting the temporary AOT configuration file.

Having all these explicit commands and runs of the Java virtual machine, with the explicit AOT configuration file, gives some users extra flexibility for complex AOT workflows. For example, a user may perform cascading training runs, where an initial AOT cache, created by a first training run, is used as input to a second training run, which then accumulates more information, ultimately leading to the creation of a second AOT cache that contains a superset of the information in the initial AOT cache.

But the flexibility and explicitness of the pair of commands comes with a cost: The simplest use case must pick a temporary file name, run the two required commands, and delete the temporary file. This is annoying and error prone, and requires the user to remember to delete the intermediate file. In fact, the deletion step may be seen as a third command, required to make even the simplest AOT cache.

Perhaps worse, the requirement to run two commands makes it harder to compose the java command with a launcher such as jruby. If the launcher is to support AOT cache creation (on behalf of its user) it must explicitly implement the necessary two-command (or three-command) workflow within its own logic.

Because of this multi-command protocol, AOT functionality is harder to learn for beginners. That is a situation analogous to the need to compile a Java program (yielding a classfile) and then run it (by referring to the classfile). Java is "paving the onramp" for beginners by reducing that other two-command workflow into a single step, with the option to directly execute Java source files in one command; see JEP 330 and related JEPs. It is reasonable to ask for an AOT reply to JEP 330.

In summary, AOT cache creation would be more available in more use cases and to a wider audience, if there were some way, for simple worflows, to make an AOT cache in one simple command.

Description

The simplest workflow to create one AOT cache must perform a series of actions:

The java VM launcher command will be extended to orchestrate this AOT workflow. To request it, the user will specify a new option of the form -XX:AOTCacheOutput=myapp.aot, which specifies that a new AOT cache is to be created in the given file.

(Note that the existing option -XX:AOTCache=myapp.aot works for both input or output, depending on mode. This new option fully disambiguates data flow direction into or out of AOT cache files.)

If an explicit AOT mode option is present, it must be one of AOTMode=auto or AOTMode=record. The default auto setting in this case implies record, which may also be specified explicitly if desired. The idea here is that when the user mentions an AOT cache output file, the AOT mode will automatically default to making a training run. What is more, the VM will then follow up with the necessary extra steps to create a new AOT cache.

Thus, we extend the meaning of the "automatic" AOT mode, which is already present by default. If a user runs a Java application, and the user also specifies an AOT cache output file (as an explicit command option), then the Java virtual machine automatically orchestrates a training run and also assembles an AOT cache when that training run exits, into the requested file.

The temporary AOT configuration information (necessarily communicated between the two activities) is managed in a temporary file, which may or may not be visible on the host file system. If the -XX:Aotconfiguration option is present on the command line, that file name will be used, and the file will be retained after the Java launcher exits. Otherwise, it will be deleted (if it ever existed) after exit.

Thus, the AOTMode and AOTConfiguration options are not needed for simple AOT cache creation. At a minimum, with the given set of default behaviors, an AOTCacheOutput option suffices to trigger creation of an AOT cache from a training run.

By default, the training run uses the same standard AOT cache installed with the java command. If an option is present like -XX:AOTCache=oldcache.aot (as defined by the previous JEP 483), then the specified file is read. In all modes which accept an AOT cache input using this option, as specified by JEP 483, a more explicit option like -XX:AOTCacheInput=oldcache.aot will also be accepted as a synonym. Before this JEP, the training run would use the less specific -XX:AOTCache=oldcache.aot for that purpose. If both options are present, the more explicit one takes effect.

Note that in AOTMode=create only, the -XX:AOTCache=newcache.aot option specifies an output AOT cache file, not an input. In that mode, the more direction may be specified explicitly as well, using the new option -XX:AOTCacheInput=newcache.aot.

The option -XX:AOTCacheInput=oldcache.aot is also useful for explicitly requesting an AOT cache input to a training run which is simultaneously reading an old cache and writing a new one. In that case, when both directions are explicit (AOTCacheInput, AOTCacheOutput) the less specific non-directional option AOTCache is ignored.

The preceding rules imply that an AOT cache is only written in two cases: With AOTMode=create, or with an explicit directional option like -XX:AOTCacheOutput=myapp.aot. Those two options can coexist in one command, of course.

Note that it is legal to specify the same file name for both input and output, in which case the VM takes care not to overwrite the AOT cache file before the training run is done with it. Specifying an AOT cache input to a training run is likely to help that training run launch faster. In the future, as AOT cache contents become richer and optimizations use more historical data, iterative accumulation of AOT caches are likely to become more important. The explicitly directional access to AOT caches, as provided by this JEP, makes a foundation for iterative workflows, in addition to the simplified "one-shot" workflows already described.

Alternatives

We could have invented a new AOT mode such as AOTMode=record+create to request the combined activity. But that would still leave ambiguity as to whether the -XX:AOTCache=myapp.aot file were to be read by training or not. In this hypothetical setting, the existing option -XX:AOTCache=myapp.aot would be insufficient to express the user's intentinons. For AOTMode=record, the option AOTCache=myapp.aot causes the training run to use a previously existing AOT cache, if the file exists. But for AOTMode=create, that option overwrites a pre-existing AOT cache with the newly assembled cache. These two meanings of AOTCache are incompatible, because they differ in direction.

Thus, having an explicit AOT output option is both necessary and sufficient to request the new behavior. Given that, we do not need a new AOTMode setting. Also, AOTMode=auto is a flexible default, that can readily support the new behavior.