Written by Roy van Rijn (royvanrijn.com) on
Sep 20, 2018 09:14:13
Part 1: Java to native using GraalVM
One of the most amazing projects I’ve learned about this year is GraalVM.
I’ve learned about this project during Devoxx Poland (a Polish developer conference) at a talk by Oleg Šelajev. If you’re curious about everything GraalVM has to offer, not just the native Java compilation, please watch his video.
GraalVM is a universal/polyglot virtual machine. This means GraalVM can run programs written in:
Javascript
Ruby
Python 3
R
JVM-based languages (such as Java, Scala, Kotlin)
LLVM-based languages (such as C, C++).
In short: Graal is very powerful.
There is also the possibility to mix-and-match languages using Graal, do you want to make a nice graph in R from your Java code? No problem. Do you want to call some fast C code from Python, go ahead.
Installing GraalVM
In this blogpost though we’ll look at another powerful thing Graal can do: native-image compilation
Instead of explaining what it is, let’s just go ahead, install GraalVM and try it out.
To install GraalVM, download and unpack, update PATH parameters and you’re ready to go. When you look in the /bin directory of Graal you’ll see the following programs:
Here we recognise some usual commands, such as ‘javac’ and ‘java’. And if everything is setup correctly you’ll see:
Hello World with native-image
Next up, let’s create a “Hello World” application in Java:
And just like your normal JDK, we can compile and run this code in the Graal virtual machine:
But the real power of Graal becomes clear when we use a third command: native-image
This command takes your Java class(es) and turns them into an actual program, a standalone binary executable, without any virtual machine! The commands you pass to native-image very similar to what you would pass to java. In this case we have the classpath and the Main class:
Now we have an executable that prints “Hello World”, without any JVM in between, just 5.6mb. Sure, for this example 5mb isn’t that small, but it is much smaller than having to package and install an entire JVM (400+mb)!
Docker and native-image
So what else can we do? Well, because the resulting program is a binary, we can put it into a Docker image without ANY overhead. To do this we’ll need two different Dockerfile’s, the first is used to compile the program against Linux (instead of MacOS or Windows), the second image is the ‘host’ Dockerfile, used to host our program.
Here is the first Dockerfile:
This image can be created as follows:
Using this image we can create a different kind of executable. Let’s create our application using the just created docker image:
This results in an executable ‘app’, but this is one I can’t start on my MacBook, because it is a statically linked Ubuntu executable. So what do all these commands mean? We’ll let’s break it down:
But we can do something cool with it using the following, surprisingly empty, Dockerfile:
We start with the most empty Docker image you can have, scratch and we copy in our app executable and finally we run it. Now we can build our helloworld image:
We’ve now turned our Java application into a very small Docker image with a size of just 6.77MB!
In the next blogpost Part 2 we’ll take a look at Java applications larger than just HelloWorld. How will GraalVM’s native-image handle those applications, and what are the limitations we’ll run into?