Maybe native executable in Quarkus is not for you, but it is awesome

Andre Oliveira
Analytics Vidhya
Published in
4 min readMar 2, 2020
https://quarkus.io

First things first, If you don't have a good machine with at least 8GB Ram, maybe generate a native executable isn't for you. To generate a native executable in a machine powerless than this is very hard and almost impossible to do on a daily base.

Summarizing the history, a tried to generate a native executable file in my two machines a Mac Air i5 and a Dell XPS i7 both with 4 GB ram and I got the same error, each time I tried:

Error: Image build request failed with exit status 137

I could only create this when I changed the configuration of my Linux machine to text mode to save all memory possible and even that way a had to wait for an hour.

I tried to figure out the minimum machine specification required and all I found was this:

Prerequisites

For compilation native-image depends on the local toolchain, so please make sure: glibc-devel, zlib-devel (header files for the C library and zlib) and gcc are available on your system.

Another prerequisite to consider is the maximum heap size. Physical memory for running a JVM-based application may be insufficient to build a native image. For server-based image building, we allow to use 80% of the reported physical RAM for all servers together, but never more than 14GB per server (for exact details please consult the native-image source code). If you run with --no-server option, you will get the whole 80% of what is reported as physical RAM as the baseline. This mode respects -Xmx arguments additionally.

Source: https://www.graalvm.org/docs/reference-manual/native-image/

I didn’t understand what that meant, but I can affirm. Generate a native executable in a 4GB ram machine is at least counterproductive. Now, Back to the technical matters.

In our first post about Quarkus, I have shown you what is Quarkus, why Quarkus can be a good option for your new project and how to use it. Today we are going to talk about native executable and GraalVM and why it improves, even more, the Quarkus performance.

Before we dive in about what it is and how to create an executable using Quarkus, It is important to learn and understand GraalVM.

According to https://www.graalvm.org:

GraalVM is a universal virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Groovy, Kotlin, Clojure, and LLVM-based languages such as C and C++.

GraalVM removes the isolation between programming languages and enables interoperability in a shared runtime. It can run either standalone or in the context of OpenJDK, Node.js or Oracle Database.

The part that matter to us is:

Native images compiled with GraalVM ahead-of-time improve the startup time and reduce the memory footprint of JVM-based applications.

This is the point that Quarkus comes back. Quarkus takes advantage of this feature and allows us to create native images improving, even more, the startup time of our application. To test this we going to use the application created in the first post and compare the startup time with a native executable version. Let’s do it, it is very easy.

Setup the environment:

# dnf (rpm-based)

sudo dnf install gcc glibc-devel zlib-devel libstdc++-static

# Debian-based distributions:

sudo apt-get install build-essential libz-dev zlib1g-dev

# MacOS

xcode-select --install

I'll continue to use docker in our examples to avoid the need the install things in our machine since we are only going to test and sure, to practice docker. That been said, we going to use a multi-stage Docker to create the native executable. A multi-stage Docker build is like two Dockerfile files combined in one, the first is used to build the artifact used by the second.

https://quarkus.io

Create a new file in src/main/docker/Dockerfile.multistage

## Stage 1 : build with maven builder image with native capabilitiesFROM quay.io/quarkus/centos-quarkus-maven:19.3.1-java11 AS buildCOPY src /usr/src/app/srcCOPY pom.xml /usr/src/appUSER rootRUN chown -R quarkus /usr/src/appUSER quarkusRUN mvn -f /usr/src/app/pom.xml -Pnative clean package## Stage 2 : create the docker final imageFROM registry.access.redhat.com/ubi8/ubi-minimalWORKDIR /work/COPY --from=build /usr/src/app/target/*-runner /work/applicationRUN chmod 775 /workEXPOSE 8080CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Finally

docker build -f src/main/docker/Dockerfile.multistage -t quarkus-test/hello .docker run -i --rm -p 8080:8080 quarkus-test/hello

Now you will see the application starting in milliseconds instead of seconds and you have a native executable for your application containing the application code, required libraries, Java APIs, and a reduced version of a VM. The smaller VM base like was said before improves the startup time of the application and produces a minimal disk footprint.

Benchmark

./mvnw compile quarkus:dev...INFO  [io.quarkus] (main) quarkus-test 1.0.0-SNAPSHOT (running on Quarkus 1.2.1.Final) started in 8.022s. Listening on: http://0.0.0.0:8080

Default application with jar file.

$ docker run -i --rm -p 8080:8080 quarkus-test/hello..INFO  [io.quarkus] (main) quarkus-test 1.0.0-SNAPSHOT (running on Quarkus 1.2.1.Final) started in 0.067s. Listening on: http://0.0.0.0:8080

The application started with native executable and inside docker image. Less than 1 second

All code is available in my GitHub:

https://github.com/gigandre/quarkus-test.git

--

--

Andre Oliveira
Analytics Vidhya

Tech Lead, Software Architect, Tech Enthusiast, Professional Scrum Master