One of the main goals while designing the Java language was to make application portability a reality. Java was designed in such a way that the same .class files created in one operating system can run seamlessly in another operating system or another computer without fail as long as the target system has a working Java Runtime Environment installed in it. Initially released Java runtimes had significantly slower performance when compared to other languages and their compilers such as C and C++. Subsequent releases of Java saw improvements at compiler level.
To give better performance at runtime, complex dynamic compilers called Just-In-Time(JIT) were introduced. JIT compilers selectively takes bytecodes of most frequently used methods in the application and convert them to native code during runtime. When method is compiled, JVM directly calls compiled method’s executable machine code rather than interpreting bytecode line by line. Due to the improvements done at compiler level in the various releases, JIT compilers run really fast these days and have enhanced the overall performance of Java applications.
But there are certain drawbacks with this approach. In case of a large and complex Java application, JIT compilers may take a long time to warm up. Java applications may contain methods that are not frequently used and hence never compiled at all. These methods may have to be interpreted while invoked. Due to repeated interpreted invocations, there could be performance problems.
Graal
Graal is an Oracle project aimed at implementing a high performance dynamic Java compiler and interpreter so that JVM based languages can achieve performance of native languages during runtime. There is a Java Enhancement Proposal [JEP 295] which has been put forward to use Graal with Java for Ahead Of Time Compilation.
Current Limitations Of AOT Compiler
It is not officially supported by Java 9 but is added as an experimental feature. Furthermore, AOT is currently restricted to 64 bit Linux based systems. Ahead Of Time compilation is done using a new tool jaotc instead of javac.
To use Ahead Of Time compilation, users need to use the same JDK for compilation and execution. Version information about jaotc used for compilation is added as a part of the libraries and are checked during load time. If the Java runtime is updated, you need to recompile the AOT compiled modules before executing them. Mismatch in jdk versions used for compilation and execution may result in application crashes.
Lambda expressions and other complex concepts of Java, which uses dynamically generated classes at runtime are not currently supported by AOT compiler. To generate shared object (.so) files, the system needs libelf to be pre-installed.
All these limitations maybe addressed in further releases in future.
Using AOT Compilation
Java AOT compiler can be invoked in same way as javac:
1 |
jaotc –output libTest.so Test.class |
To execute the above AOT compiled binaries, we can execute:
1 |
java –XX:AOTLibrary=./libTest.so Test |
During startup, JVM’s AOT init module looks for shared libraries in known locations or those libraries specified using -XX:AOTLibrary construct. If there are no shared libraries, AOT will be turned off for the current JVM instance.
Important Runtime Flags And Options For AOT Support
Flag | Description |
-XX:+/-UseAOT | Use or don’t use AOT compiled files. By default, this flag is turned on. |
-XX:AOTLibrary | One or more AOT compiled .so files can be specified with this tag. If there are more than one libraries, they are separated using a colon ( : ) |
-XX:+/-PrintAOT | Enable/disable printing of used AOT classes and methods. |
-XX:+/-UseAOTStrictLoading | Enable/disable AOT strict loading. If it is enabled, it directs JVM to exit if any of the AOT libraries have a configuration different from the current runtime configuration. |
–class-name | List of java classes to compile. |
–jar | List of jar files to compile. |
–module | List of java modules to compile. |
–directory | List of directories where files to compile can be found. |
aotclassload | Creates a log when corresponding class is found in the .so file. |
exclude | Exclude the specified methods during compilation. |
compileOnly | Compiles only the specified methods. |
compile-for-tiered | Generates compiled code with profiling. By default, profiling is not done. |
exit-on-error | Exits on compilation error. |
info | Prints information on compilation phase. |
debug | Allows debugging. |
help | Help menu for jaotc. |
version | Prints version of jaotc. |
compile-with-assertions |
Generate compiled code with assertions. By default, assertions are not created.
|
Outcomes Of AOT
It is not always guaranteed that all applications will benefit from AOT compilation. Various limitations of current AOT compiler implementation in Java 9 were listed earlier in this article. If a user finds that an application created after compiling with jaotc starts up with a slower performance than usual, he is advised to switch off AOT compilation using -XX:-UseAOT and then recompile using javac compiler.
is there a plan to support (not experimental but complete native support) AOT in Java 10?
err.. I meant to ask about Java 11. I checked the road map for Java 11, but it does not seem to give any clear direction with regard to AOT.
Have you taken a look at Project Metropolis. Running Graal to statically compile Java code in a “native compilation mode” to prepare JVM components that can replace C++ components. (This will extend existing work with AOT and/or the Substrate VM.)