Raphael Habereder
Posted on June 4, 2020
The myth of slow Java
In the realm of Java, who hasn't heard one of these statements at least a dozen times:
- "Java is slow"
- "Java needs too much memory!"
- "Java Services/Servers need too long to start!"
- "Java isn't ready for microservices!"
I'm glad to tell you, with this post, that none of these are true anymore. Actually, they haven't been true anymore for a long time.
The last time I had a slow boot of an old-school Application Server was in the age of Tomcat or JBoss 4.x, and those are very, very old.
The solution to a non-existing problem
The - hopefully final - nail in the coffin of these myths is named Quarkus, or as the people behind this wonderful creation call it: "Supersonic Subatomic Java".
So what is it actually? Again, let's use the words of the creators: "A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards."
Alright, that doesn't really tell us much, it also introduces another Buzzword: GraalVM.
GraalVM? What is that now?
Oracle created something wonderful with GraalVM, here is their sales-pitch on it:
"GraalVM is an ecosystem and shared runtime offering performance advantages not only to JVM-based languages such as Java, Scala, and Kotlin, but also to other programming languages such as JavaScript, Ruby, Python, and R. Additionally, it enables the execution of native code via an LLVM front-end, and WebAssembly programs on the JVM."
And let me tell you, those are not just empty promises! GraalVM is amazingly fast and combined with Quarkus, you are in for one hell of a joy-ride.
We want the numbers Mason!
While evaluating Quarkus for a Microservice Landscape, we did our homework first and compared three different environments:
- Payara Micro + JVM
- Quarkus + JVM
- Quarkus Native Image
While Payara Micro is already considered "fast" and "small footprint", Quarkus and a Quarkus Native Image respectively, are considerably faster and even smaller in overhead.
Don't believe me? I'll back those claims up in just a second.
Here are some pictures of a loadtest we created. The application was equipped with JAX-RS and JPA, targeting a (brutally oversized) in-memory database (so we can reduce the impact of database speed or, god forbid, slow network traffic).
The test itself consisted of 5000 Requests in 5 concurrent threads and a minimal database payload (it was just a string tbh).
All we monitored were CPU-Load, Time and Memory-Usage of the service image itself.
It's not really a detailed or scientific test, but the numbers are sufficient enough to name a clear winner.
Payara Micro with OpenJDK 11.0.6 (Provided by GraalVM 20.0 CE):
Quarkus with OpenJDK 11.0.6 (Provided by GraalVM 20.0 CE):
Amazing how fast a native Image can be, right?
As you can see, we got the following advantages, just by using quarkus native images:
- we reduced the time to first request from 7.3 seconds, to 0.104 seconds. This would typically include the costly bootstrapping of a jvm/application-server runtime, as well as the preparation of the JAX-RS Endpoint
- memory usage decreased from ~700 MByte to ~50 MByte
- the CPU-Load went from a wild rollercoaster of 50%-90% to consistent 30% during the whole lifecycle
While working off the 5000 Requests hasn't sped up as drastically, the memory consumption consistently stayed very low.
If that isn't a noticeable improvement, I don't know what is.
So let's build something with Quarkus!
A demo project, including the following Dockerfile can be found here
Are you ready to dive in? So here we go!
First a basic Dockerfile to build some Java stuff
FROM quay.io/quarkus/centos-quarkus-maven:19.3.1-java11 AS build
# Since JVM has not been ported to alpines musl yet
# and quarkus still relies on gcc for native binaries
# We'll use the quarkus maven image as build-base
# Lots of workarounds and setup for graalvm and quarkus to build the native binary
# thankfully none of them end up in the final image
WORKDIR /usr/src/app
USER root
RUN chown -R quarkus /usr/src/app
USER quarkus
COPY --chown=quarkus app/ .
RUN ./mvnw clean package -Pnative
# Due to the very same reason of musl not playing along just yet
# we will use the redhat minimal image for delivery
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /app
# Since the user should always be nobody (imho) and the USER-Directive only affects RUN, CMD and ENTRYPOINT
# But not WORKDIR, we have to modify ownership of the workdir
RUN chown nobody. /app
# Copy as nobody
COPY --from=build --chown=nobody /usr/src/app/target/*-runner /app/quarkusapp
# Set up permissions for user `nobody`
RUN chmod 775 /app \
&& chmod -R "g+rwX" /app \
&& chown -R nobody. /app
EXPOSE 8080
USER nobody
# Tell quarkus to listen on all interfaces, instead of localhost
CMD ["./quarkusapp", "-Dquarkus.http.host=0.0.0.0"]
This handles the build part for you.
You might be asking yourself now "Yeah, this is nice and all. But what if I need more than just your lame little JAX-RS Service? This doesn't really help me much..."
Don't worry friend, quarkus has a bootstrapping service, like most other cool cloud-ready frameworks today, where you can check some boxes and get the skeleton project ready to use!
All this does basically, is change the dependencies in the resulting pom.xml, which the quarkus-maven-plugin picks up and builds your image with.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
With this short, but hopefully sweet, little introduction, you should be ready to go and quarkus(ify?) your applications. And don't forget, always try to have fun doing it!
If I missed anything or you need some further information, don't shy away from leaving a comment, I'll make sure to leave none of you hanging :)
Posted on June 4, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.