JEP 295: Ahead-of-Time Compilation
Summary
Compile Java classes to native code prior to launching the virtual machine.
Goals
-
Improve the start-up time of both small and large Java applications, with at most a limited impact on peak performance.
-
Change the end user's work flow as little as possible.
Non-Goals
It is not necessary to provide an explicit, exposed library-like mechanism for saving and loading compiled code.
Motivation
JIT compilers are fast, but Java programs can become so large that it takes a long time for the JIT to warm up completely. Infrequently-used Java methods might never be compiled at all, potentially incurring a performance penalty due to repeated interpreted invocations.
Description
AOT compilation of any JDK modules, classes, or of user code, is experimental and not supported in JDK 9.
To use the AOTed java.base
module, the user will have to compile the module and copy the resulting AOT library into the JDK installation directory, or specify it on java command line. The usage of AOT-compiled code is otherwise completely transparent to end users.
AOT compilation is done by a new tool, jaotc
:
jaotc --output libHelloWorld.so HelloWorld.class
jaotc --output libjava.base.so --module java.base
It uses Graal as the code-generating backend.
During JVM startup the AOT initialization code looks for well-known shared libraries in a well-known location, or as specified on the command line with the AOTLibrary flag. If shared libraries are found, these are picked up and used. If no shared libraries are found then AOT will be turned off for this JVM instance.
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld
New java AOT flags and jaotc
flags are listed in following subsections which also have instructions on how to build and install the AOT library for the java.base
module.
The container format used for AOT-compiled code is shared libraries. The JDK 9 version only supports Linux/x64, where the shared library format is ELF. AOT-compiled code in AOT libraries is treated by JVM as extension of existing CodeCache. When a java class is loaded JVM looks if corresponding AOT-compiled methods exist in loaded AOT libraries and add links to them from java methods descriptors. AOT-compiled code follows the same invocation/deoptimization/unloading rules as normal JIT-compiled code.
Since class bytecodes can change over time, either through changes to the source code or via class transformation and redefinition, the JVM needs to detect such changes and reject AOT-compiled code if the bytecode doesn't match. This is achieved with class fingerprinting. During AOT compilation a fingerprint for each class is generated and stored in the data section of the shared library. Later, when a class is loaded and AOT-compiled code is found for this class, the fingerprint for the current bytecode is compared to the one stored in the shared library. If there is a mismatch then the AOT code for that particular class is not used.
The same JDK should be used during AOT compilation and execution. Java version is recorded in AOT libraries and checked during their load. AOT recompilation is required when Java is updated.
jaotc
does not resolve referenced classes which are not system classes or part of compiled classes. They have to be added to class path. Otherwise ClassNotFoundException could be thrown during AOT compilation.
jaotc --output=libfoo.so --jar foo.jar -J-cp -J./
jaotc --output=libactivation.so --module java.activation -J--add-module=java.se.ee