, 2 min read

Compiling Java source to binary (native)

Original post is here eklausmeier.goip.de/blog/2020/12-19-compiling-java-source-to-binary-native.


With GraalVM you can now fully compile a Java file to a native binary. This is also called AOT, ahead-of-time compilation. Compilation is very slow, and resulting binary is huge as it must contain all code which might be referenced. In contrast the class file usually is quite small. Though, it is advantageous that the resulting binary starts way faster.

From the GraalVM web-page:

The Native Image builder or native-image is a utility that processes all classes of an application and their dependencies, including those from the JDK. It statically analyzes these data to determine which classes and methods are reachable during the application execution. Then it ahead-of-time compiles that reachable code and data to a native executable for a specific operating system and architecture. This entire process is called building an image.

Assume the simple program:

public class hello {
    public static void main(String argv[]) {
        System.out.println("Hello world.");
    }
}

Compiling this simple program is quite slow:

$ time /usr/lib/jvm/java-11-graalvm/bin/native-image hello
[hello:90709]    classlist:   1,173.69 ms,  0.96 GB
[hello:90709]        (cap):     746.78 ms,  0.96 GB
[hello:90709]        setup:   2,072.73 ms,  0.96 GB
[hello:90709]     (clinit):     214.77 ms,  1.22 GB
[hello:90709]   (typeflow):   5,433.03 ms,  1.22 GB
[hello:90709]    (objects):   4,402.72 ms,  1.22 GB
[hello:90709]   (features):     281.83 ms,  1.22 GB
[hello:90709]     analysis:  10,615.01 ms,  1.22 GB
[hello:90709]     universe:     486.71 ms,  1.71 GB
[hello:90709]      (parse):   1,237.17 ms,  1.71 GB
[hello:90709]     (inline):   1,174.69 ms,  1.71 GB
[hello:90709]    (compile):   7,934.95 ms,  2.35 GB
[hello:90709]      compile:  10,857.38 ms,  2.35 GB
[hello:90709]        image:   1,052.94 ms,  2.35 GB
[hello:90709]        write:     174.08 ms,  2.35 GB
[hello:90709]      [total]:  26,598.83 ms,  2.35 GB
real 27.23s
user 145.40s
sys 0
swapped 0
total space 0

Compilation will start a huge number of threads. Resulting binary is ca. 9 MB.

In Arch Linux the native compiler is contained in native-image-jdk11-bin, which in turn needs jdk11-graalvm-bin.

Running the binary is way faster than starting the class file within JVM. Starting the binary.

$ time ./hello
Hello world.
real 0.00s
user 0.00s
sys 0
swapped 0
total space 0

Starting the class file in JVM.

$ time java hello
Hello world.
real 0.07s
user 0.07s
sys 0
swapped 0
total space 0

For building native images on Windows follow the instructions here: Prerequisites for Using Native Image on Windows.

Contrary to initial conception, GraalVM is not automatically faster during runtime. It is clearly way faster during startup. Michael Larabel conducted a performance test on GraalVM 20.1, OpenJDK 11, OpenJDK 14.0.1, OpenJDK 15, and others. Result: Sometimes GraalVM is faster, sometimes not.

null

SciMark #2