Java AOT Compilers – How Do They Work?

aotcompilerjava

There are a few number of tools out there (Excelsior JET, etc.) that claim to transform Java app's into native executables (*.exe). However it is my understanding that these tools are really just creating native wrappers that invoke/execute java from a shell or command-line.

If that understanding is incorrect, I don't see how it could be. If a running JVM (java process) is essentially a high performance interpreter, loading bytecode from Java classfiles on the fly, then I don't see how a Java app (a collection of bytecode files that serve as input to a JVM) could ever be truly converted into an executable.

This is because the JVM process is already a native executable that takes sets of bytecode files as input. To merge those bytecode files and the JVM process into a single, unified native executable doesn't seem possible without completely rewriting the JVM and de-railing from the JVM specification.

So I ask: how do these tools actually "transform" Java class files into a native executable, or do they?

Best Answer

All programs have a runtime environment. We tend to forget this, but its there. Standard lib for C that wraps system calls to the operating system. Objective-C has its runtime that wraps all of its message passing.

With Java, the runtime is the JVM. Most of the Java implementations that people are familiar with are similar to the HotSpot JVM which is a byte code interpreter and JIT compiler.

This doesn't have to be the only implementation. There is absolutely nothing saying you can't build a standard lib-esque runtime for Java and compile the code to native machine code and run that within the runtime that handles calls for new objects into mallocs and file access into system calls on the machine. And thats what the Ahead Of Time (AOT rather than JIT) compiler does. Call that runtime what you will... you could call it a JVM implementation (and it does follow the JVM specification) or a runtime environment or standard lib for Java. Its there and it does essentially the same thing.

It could be done either by reimplementing javac to target the native machine (that's kind of what GCJ did). Or it could be done with translating the byte code generated by javac into machine (or byte) code for another machine - that's what Android does. Based on Wikipedia that's what Excelsior JET does too ("The compiler transforms the portable Java byte code into optimized executables for the desired hardware and operating system (OS)"), and the same is true for RoboVM.

There are additional complications with Java that means this is very hard to do as an exclusive approach. Dynamic loading of classes (class.forName()) or proxied objects require dynamics that AOT compilers do not easily provide and so their respective JVMs must also include either a JIT compiler (Excelsior JET) or an interpreter (GCJ) to handle classes that couldn't be precompiled into native.

Remember, the JVM is a specification, with many implementations. The C standard library is also a specification with many different implementations.

With Java8, a fair bit of work has been done on AOT compilation. At best, one can only summarize AOT in general within the confines of textbox. However, in the JVM Language Summit for 2015 (August of 2015), there was a presentation: Java Goes AOT (youtube video). This video is 40 minutes long and goes into many of the deeper technical aspects and performance benchmarks.

Related Topic