Feature #11169
Running Ahead-of-time compilation (AOT) on FWD
0%
Related issues
History
#1 Updated by Teodor Gorghe 6 months ago
Java Ahead-of-time compilation was introduced in JDK 25, as an evolution of AppCDS, which optimizes the warmup time of application even more.
In addition to AppCDS, AOT does the following things:
- Metadata (Class Loading & Linking)
Without AOT: The JVM must find String.class on disk, read the bytes, parse them, verify they are safe, and allocate C++ structures in memory to represent "String". It does this for 10,000+ classes (Netty, Spring, etc.) every single time you start.
With AOT: The JVM takes a "snapshot" of all these internal C++ structures. When you start, it mmaps (memory maps) this file directly into memory. It is effectively instant.
- Heap optimization
Without AOT: Your application runs static { ... } blocks. It calculates constants, interns Strings, and builds look-up maps.
With AOT: The JVM runs this code once during the "Record" phase. It captures the resulting objects (the "Heap") and saves them to the file. On the next run, those objects are pre-loaded into the heap.
- Dynamic Linking (Behavior), which is the most important in FWD
Without AOT: When you use a Lambda (stream.map(x -> x + 1)), the JVM has to "spin" (generate) a special class at runtime to handle it. This consumes CPU and slows down the first few seconds of your app.
With AOT (Dynamic CDS): The JVM records these generated classes. On the next run, it knows exactly what the Lambda class looks like and loads it immediately without spinning the bytecode generator.
This involves some steps, similar with AppCDS:
- Record the AOT cache configuration:java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -cp ... com.goldencode.p2j.main.FwdLauncher ...
- Warm the application and navigate through most of scenarios
- Close the application
- Build the AOT cachejava -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot -cp ... com.goldencode.p2j.main.FwdLauncher ...
- If the cache build was successful, run the application with AOT cache:java -XX:AOTCache=app.aot -cp ... com.goldencode.p2j.main.FwdLauncher ...
The main issue right now is that there seems to be a bug in JDK 25 about java.util.concurrent.ConcurrentHashMap. This makes the AOT cache building process to fail:
[error][aot,heap] Reference trace
[error][aot,heap] [ 0] {0x000000064ec15bd8} jdk.internal.util.ReferencedKeySet::map (offset = 12)
[error][aot,heap] [ 1] {0x000000064ec15be8} jdk.internal.util.ReferencedKeyMap::map (offset = 16)
[error][aot,heap] [ 2] {0x000000064ec15c00} java.util.concurrent.ConcurrentHashMap
[error][aot,heap] Cannot archive the sub-graph referenced from [Ljava.util.concurrent.ConcurrentHashMap$Node; object (0x000000065170e800) size 262160, skipped.
[error][aot] An error has occurred while writing the shared archive file.
I have managed to mitigate this issue by disabling invokedynamic bytecode instruction optimization, with -XX:+UnlockDiagnosticVMOptions -XX:-AOTInvokeDynamicLinking
I have done with some tests with one customer customer application and I am noticing some clear improvements when the FWD server is cold.
What needs to be done for improving AOT even more:
- try to enable invokedynamic and mitigate the bug from JDK25.
- there are some classes which are not included in AOT cache (AST compiled expressions, objects which are being invoked from ReflectASM). ReflectASM is an old library and it was being replaced with native Java Reflection API on ControlFlowOps, but there are still some places where it is still being used. The openjdk AOT does not have a knowledge where invoked ReflectASM classes comes from and it skips inserting into AOT cache.
#2 Updated by Teodor Gorghe 6 months ago
- Related to Feature #9686: move FWD to Java 25 added