Pierre Guimon
Posted on October 4, 2022
Introduction
The world of Java toolkits to build applications is already rich, and Quarkus is yet another toolkit.
Why should you be interested in it? We will see what makes Quarkus different from other Java toolkits.
We will first focus on defining what is Quarkus.
Then, we will see Quarkus internal architecture, what technical benefits it brings, and how it can be more performant than the average Java toolkit.
This article tries to give you an overview of Quarkus framework, as well as explaining its interactions with GraalVM.
It is inspired by some of Emmanuel Bernard talks on the subject who is a recognized Java champion and lead at Quarkus RedHat project.
What is Quarkus ?
Quarkus definition
Quarkus is, the contraction of the words quark and us. Quark, the elementary particle and fundamental constituent of matter, and us. If you go to the Quarkus website, at the top, you can read two sentences:
- Supersonic Subatomic Java
- A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards
Supersonic Subatomic Java
- The idea behind supersonic is speed.
As we will see, Quarkus does all its best to boot your application as fast as possible and to respond to the first request.
Another interesting aspect of the speed is the way you develop. With Quarkus, a developer codes and tests its code. It's so fast in recompiling and starting that, as a developer, you save a lot of time just by changing your code and seeing it running.
Without wasting time, developing with Quarkus makes the developer's experience feel much faster.
- The idea behind the word subatomic is the size.
The size of the memory used to run an application, but also the size of the executable once the application is packaged.
Quarkus is optimized to reduce the amount of memory used by a Java application. By using several tricks that we will discover next, Quarkus not just reduces the amount of memory of a Java heap, but the memory used by the entire process (RSS: Resident Set Size).
Since the beginning of the Java platform, we were able to package an entire application into a binary file. Thanks to GraalVM, Quarkus drastically reduces the size of the binary. All in all, Quarkus is low in resource consumptions.
- And finally, Java.
Quarkus is polyglot, as it supports several JVM languages and, thanks to GraalVM, can also support all the languages such as C, C++, Ruby, or JavaScript. But Quarkus is Java first.
Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards
- Kubernetes Native.
From the beginning, Quarkus has been designed around the container first philosophy.
By producing small binaries with fast startup, Quarkus is perfectly suited for orchestration platforms like Kubernetes.
Quarkus comes with Docker images and Kubernetes extensions to easily package and deploy applications.
By supporting Kubernetes natively, Quarkus has been a so‑called Knative and cloud native platform since its creation. Of course Quarkus can deploy your application in any environment, but it keeps Kubernetes in mind.
- Java HotSpot and GraalVM.
HotSpot comes first in the definition because it's important for Quarkus to target the mostly used JVM.
But Quarkus brings also support for GraalVM. GraalVM support has been an important part of the design of Quarkus from the beginning.
When an application is compiled down to a native binary, it starts much faster and can run with a much smaller heap than a standard JVM.
- Java libraries and standards.
Quarkus is a new toolkit, but its programming model isn't new.
In fact, it builds on top of proven standards such as Eclipse MicroProfile or frameworks such as Vert.x or JAX‑RS.
Instead of reinventing the wheel on common technical use cases, Quarkus prefers to integrate well‑known libraries.
In fact, it integrates with hundreds of libraries. But Quarkus is not limited to standards or known libraries. If you have an in‑house library running on top of a JVM then it will work on Quarkus.
Open source
It is important to mention that Quarkus is open source under an Apache 2 license, and all its code is on GitHub at github.com/quarkusio.
Anybody can join and participate to Quarkus development.
Quarkus is about supporting the best Java libraries and standards through its extension mechanism, but it is also an ecosystem of ever growing extensions.
This never‑ending ecosystem is called Quarkiverse. Quarkiverse is a GitHub organization providing repository hosting, including build, continuous integration, and deployment of Quarkus extensions, mostly contributed by the community.
Operational concern: Cloud / on-premise
Java is born in 1995 and at that time was mostly used to write graphical applications, such as applets. The language was based on the available hardware, using single core CPUs and multi‑threads.
Quickly, the language moved to the server side, and we started developing monolithic applications designed to run on huge machines 24x7 for months, even years, with lots of CPU and memory.
The JVM startup time was not an issue. The memory used by the JVM was huge, but we just let the just‑in‑time compiler optimize the execution over time and let the garbage collector manage the memory efficiently.
Today, we don't have huge machines, we have small ones that we can easily discard. We moved from single cores, multi‑threads to multi‑cores, and we tend to be careful on the number of threads as they consume a lot of resources.
Slow startup time and resource consumptions don't fit well in our new environment where we need to deploy hundreds of microservices into the cloud, move them around, and stop and start them quickly.
Instead of scaling an application by adding more CPU and memory, we now scale microservices dynamically by adding more instances.
Today, we need small binaries with small footprints and low resource consumptions.
So the industry went from running a monolith on a huge machine to scaling up and down smaller microservices or functions on several small servers, to orchestrating tiny functions moving around constantly, having to start in a few milliseconds to handle a request and stopping immediately.
Java wasn't suited for this new environment.
Today, being on the cloud or on‑premise, applications are usually deployed on a container platform. On top of these platforms, you can run so many JVMs.
JVMs consume resources, memory, startup time, so the density is low. That's why we've seen other technologies and languages emerging in the cloud, such as Node.js or native languages such as Go.
For the same amount of resources, the density of applications written in Go is much higher than in Java.
The goal of Quarkus is to get the same density, but using Java platform instead of moving to a new one or a new language. What this means is that Quarkus is optimized for low memory usage, small binaries, and fast startup time.
Quarkus allows to benefit for better operational model both in cloud and on-premise data center.
Where does it come from ?
Quarkus is driven by Red Hat. In 2006, Red Hat acquired JBoss, who was developing the JBoss application server, and this way entered the Java ecosystem.
Then, Red Hat started to work on Java's Standard Edition by committing to the OpenJDK, but also the Java Enterprise Edition or the MicroProfile.
Recently, Red Hat started to get involved in GraalVM and distribute its own addition called Mandrel. It also started hosting application with its own cloud platform called OpenShift.
Quarkus came from an open source company who was a Linux operating system, commits to the JDK and GraalVM and host application to the cloud, the same company that gives Quarkus production support.
The company JBoss was created in 1999 and started developing the JBoss Application Server, later known as JBoss EAP, for Enterprise Application Platform.
In 2014, JBoss Application Server was renamed WildFly. WildFly, for the free and open source software, and the name JBoss Application Server stayed for the supported product.
In 2015, Red Hat created this innovative approach to packaging and running Java Enterprise Application. It was called WildFly Swarm and then renamed to Thorntail. Thorntail didn't last long, and its end of life was announced in 2020.
But Thorntail brought a set of new ideas that were introduced in Quarkus in 2018. 2018 was the year of the first Quarkus public commit.
Quarkus 1.0 was then released in 2019, and since then has evolved at a rapid pace, nearly one release per month.
Quarkus entered version 2.1 in June 2021. Even if Quarkus was created in 2018, it comes from a company that has a long history of open source, Java runtimes, distributed environments, microservices, and cloud environment.
How does Quarkus work ?
GraalVM
Before talking about the internal architecture of Quarkus, we need to present GraalVM.
Quarkus targets the HotSpot VM of course, but it was also built with GraalVM in mind.
GraalVM is an extension of the Java Virtual Machine to support more languages and several execution modes. GraalVM is itself implemented in Java.
Running your application inside a JVM comes with startup and footprint costs. To improve that, GraalVM has a feature to create native images for existing Java applications.
This improves the performance of Java to match the performance of native languages for fast startup and low memory footprint.
This is the entire spectrum of GraalVM:
- Graal compiler
At the heart of GraalVM comes the Graal compiler.
The Graal compiler is a high‑performance compiler written in Java. It accepts the JVM bytecode and produces both dynamic and static compilation for native code.
Here we are not talking about the Java compiler written in C which takes the Java code and compiles it into bytecode.
On the dynamic side, it uses the new JVM compiler interface to communicate with the good old job HotSpot VM.
- Java HotSpot VM
The HotSpot has a just‑in‑time compiler known as JIT, which starts interpreting the code and then compiles it. The HotSpot supports all the known JVM languages, such as Java, Scala, or Groovy.
- Substrate VM
On the other side, for the static compilation, the Graal compiler relies on Substrate VM. Substrate VM allows ahead‑of‑time compilation or AOT for applications written in various languages. This ahead‑of‑time compilation improves the startup time by loading precompiled classes.
With Substrate VM everything is compiled so JIT is not necessary, as well as metadata around code usage. Classes are compiled so there is no need to keep information for dynamic compilation and linkage.
There is a garbage collector which is simpler than the OpenJDK ones. RedHat and other entities are currently working on GC algorithm improvements for Substrate VM.
- Truffle framework
On top of all that, you will find the Truffle framework that enables you to build interpreters and implementations for other languages, such as R, Ruby, JavaScript, or Python.
- Sulong
Then comes Sulong for what we call LLVM‑based languages, such as C, C++, or Fortran.
When we talk about Quarkus on GraalVM we are talking about the following parts:
Let's focus on what interests Quarkus the most: ahead‑of‑time compilation.
When developing a typical Java application, you have your own business classes. You rely on external libraries and the JDK classes.
To get native images, you also need to include the necessary components of Substrate VM. Typically, these components are memory management or thread scheduling.
At the end, your application consists of thousands and thousands of classes. Before compiling natively, the image generation process employs static analysis to find any code reachable from the main Java method.
These reachable classes form what's called the closed world. Then, it's just a matter of eliminating all the classes and methods that are not used by your application. This is called dead code elimination.
Then, thanks to ahead‑of‑time compilation, the remaining Java code is compiled into a standalone executable called a native executable or native binary for the platform you are running into (MAC, Linux, etc...).
Thanks to dead code elimination, the final executable is smaller. And thanks to ahead‑of‑time compilation, the resulting native binary contains the application in machine code ready for its immediate execution.
The end result is an application that is faster to start and uses a smaller amount of memory. This is why Quarkus is a great runtime for containers, as well as cloud native and serverless deployments.
Internal Quarkus architecture
Quarkus can run on both JVM, the HotSpot with its just‑in‑time compiler, and GraalVM VM with ahead‑of‑time compilation.
Quarkus does a lot of things from persistence to transaction to microservices, reactive messaging, and so on.
So you might think that its core is huge and implements hundreds of features. Well, this is not the case, Quarkus is made of a small core on which relies hundreds of extensions and from an end-user point of view we only see Quarkus as a maven or gradle dependency.
In fact, the power of Quarkus is its extension mechanism. Persistence, transaction, fault tolerance, security are all external extensions that can be added to your application only if needed.
And next to these extensions, you can add an infinite number of external third‑party or in‑house libraries.
The core of Quarkus and this extension mechanism is heavily based on Arc, a lightweight dependency injection framework.
The core of Quarkus also does the hard work of rewriting parts of the application at build time.
For that, it uses a set of tools such as Jandex, which is the Java annotation indexer and reflection library to optimize annotation processing.
Gizmo is a library used to produce Java bytecode. And thanks to the Graal SDK, Quarkus can use a single path, single class loader, and dead code elimination mechanism. Quarkus uses Jandex to index classes and Gizmo to produce bytecode, but at build time, not at runtime.
Build time vs runtime
Quarkus is the way for frameworks to embrace GraalVM idiomatics.
It requires to change the way frameworks work, not really at runtime but at startup time. Most of the dynamicity that a framework brings actually comes at the startup time and this is what is being shifted to the build time with Quarkus.
Quarkus could be also highlighted to be a framework to makes frameworks start at build time.
At startup time a framework (like Hibernate or Spring for instance) does usually the following:
- Parse config files (e.g.: persistance.xml file for Hibernate)
- Classpath and classes scanning for annotations (e.g.: Entity, Bean, etc...), getters or others metadata
- Build metamodel objects from all those above information on which the framework will run at runtime. For instance Hibernate doesn't keep .xml files in memory but builds an internal model that is represented at runtime and it is this model that is used at runtime to save entities, etc...
- Prepare reflection (will get the reference to method object and field to be able to perform invoke) and build proxies
- Start and open IO, threads, etc... (e.g.: database connection, etc...)
Conceptually, when you look at those steps, there could be easily done at build time instead of at startup time.
Everything that is prior to the last step and even some parts of the start can be done at build time.
This is what is done by Quarkus, it takes a framework like Hibernate and makes it work so that the maximum of steps can be performed at build time.
On the following schema, you can see a typical Java framework at the top where most of the work is performed at runtime (configuration load, classpath scanning, model creation, starts the management), whereas at the bottom you can see a Quarkus framework where most of the work is performed at build time.
This represents the Quarkus build process:
There are several advantages to this approach:
- You do this work only once at compilation time and not at every startup.
- The percentage of classes/lines of code that are specialized for the startup time in a framework can be very high. Since the work is done at build time, these classes, can be removed by the dead code elimination, or not loaded when running on HotSpot VM. Hence the startup time and the memory consumption are lower.
- In the JVM, one thing that is particularly slow is to load classes and scan them. Because loading the bytecode means that we need to verify it, etc... without even having initializing the classes.
- In Quarkus, Jandex tool performs a quick JARs indexing by only reading the files without analyzing the bytecode via metadata: what is the class, what are the subclasses, what is the methods list, what are the return values, are there any annotations, etc...
- Every extension uses Jandex so that instead of having to load the classes we only read those metadata. This allows to gain time.
- Another trick which is applied is the following: with Quarkus, since most of the operations are performed at build time, there are steps like bytecode improvement/re-writing of bytecode that can be done at build time and in one shot. Indeed, all the improvements on the bytecode are listed and for one given class, we apply them all-at-once instead of having to re-process the same class for multiple bytecode improvement like it's usually done.
- With GraalVM, static initialization is performed at build time, this approach is also used by Quarkus. For instance, for Hibernate framework, everything that can be initialized before accessing the database, is put in a static block that will be executed by GraalVM at compilation time. All this work will be included in the binary. When the application is started, this step as already been performed and the heap is put in memory and we can continue with further steps of the initialization. Quarkus really embraces this ahead-of-time compilation approach of GraalVM. This is also a game changer for Hotspot VM, since it can benefit from those major optimizations.
Extensions
You can build any kind of application thanks to the Quarkus extension mechanism.
As we've seen previously, Quarkus uses an extension mechanism, but Quarkus makes the difference between extensions and external libraries.
First of all, extensions are developed and maintained by the Quarkus team. You can find them on the Quarkus GitHub repository. They integrate seamlessly into the Quarkus architecture as they can be processed at build time and be built in native mode with GraalVM.
External dependencies are any in‑house Java framework that you've developed and maintained or external Java libraries that you can find out there.
Therefore, they are not maintained by the Quarkus team GraalVM being very aggressive with dead code elimination, some of these external libraries might not work out of the box with native compilation.
You might need to recompile them with the right GraalVM configuration to make them work. That's why not every external library is an extension.
To sum up, Quarkus works with any external library in JVM mode so you are not restricted to Quarkus extensions. But you are not sure that an external library will work on native mode and will be optimized at build time.
Quarkus has hundreds of extensions, and every release brings new ones.
One way to keep up to date is to go to code.quarkus.io and check if the technology or framework that you are looking for has been integrated as a Quarkus extension.
If it's not the case, remember that you can always use it, but as an external library. And that might not compile with GraalVM or be optimized for Quarkus, but it will work.
Here is an overview of available popular extensions:
Web extensions
Quarkus supports the good old servlet, as well as RESTEasy to develop RESTful web services. This goes hand in hand with the JSON binding and JSON processing extensions.
It also has an OpenAPI extension for documenting REST endpoints, gRPC, or GraphQL.
Database
Remember that Quarkus comes from Red Hat, the company behind Hibernate and Narayana, the robust transaction manager.
So you will get Hibernate ORM for relational mapping, Hibernate Validator to validate data, and Hibernate Envers to have historical data.
Quarkus supports several relational database JDBC drivers, such as PostgreSQL, MariaDB, SQL Server, or H2, as well as MongoDB, Amazon S3. or Elasticsearch.
Messaging
In terms of messaging, Quarkus has a JMS extension, but also supports new messaging brokers, such as Kafka or Kafka Streams.
It also has an extension for AMQP, as well as MQTT, which is the standard messaging protocol for the Internet of Things.
Reactive
In terms of reactive architectures, Quarkus goes all the way from the database to exposing reactive REST endpoints.
This is because it uses Vert.x extensively and reactive programming to get reactive messaging, reactive REST endpoints with RESTEasy, and database access with Hibernate Reactive thanks to the support of reactive R2DBC drivers.
Cloud
Being a Kubernetes‑native stack, Quarkus comes with a few extensions to build Docker images, such as Docker itself or Jib.
As well as Kubernetes and Minikube extensions to easily orchestrate microservices on your development and production environment.
It has support for cloud providers, such as OpenShift, AWS, Azure, or Google Cloud, and comes with an extension called Funqy to develop functions on top of AWS Lambda and Azure Functions.
Monitoring
When you have several microservices or functions, you need to observe them. For that, Quarkus comes with a health check extension, as well as metrics and distributed tracing.
Security
In terms of security, you can use OpenID Connect or JSON Web Token very easily. Quarkus comes with a set of extensions to integrate Elytron, Keycloack, or Vault.
Where to begin ?
I encourage you to go on the Quarkus website on which you can find a link to the coding Quarkus website.
You will be able to get hands-on Quarkus via sample applications that are package for you, you just need to unzip them and import them in your favorite IDE.
You will experience the fast startup, live reload, low memory footprint that comes with Quarkus.
After having tried out some of the Quarkus sample applications, I encourage you to have a look at the Quarkus guides.
You will find all possible information around core Quarkus mechanism such as CDI, etc... as well as guides for the most common extensions such as RESTeasy, Hibernate, Apache Kafka, etc...
References
- Emmanuel Bernard talk on Quarkus DEVOXX video: Quarkus why, how and what
- Oleg Selaje & Thomas Wuerthinger talk on GraalVM DEVOXX video: Everything you need to know about GraalVM
- For FR speakers: Emmanuel Bernard & Clément Escoffier talk on using Quarkus with GraalVM DEVOXX video: Quarkus: Comment faire une appli Java Cloud Native avec Graal VM
Posted on October 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.