Quarkus, a relatively new Java framework, has rapidly gained popularity due to its innovative approach to building container-native applications. It combines the best of both worlds: the productivity of Spring Boot and the performance of GraalVM. This unique blend has positioned Quarkus as a compelling choice for developers seeking to create efficient, scalable, and cloud-native applications.
Understanding Quarkus: A Brief Overview
Quarkus is a framework designed to build fast, efficient, and container-native Java applications. It leverages GraalVM's ahead-of-time (AOT) compilation to produce native executables, resulting in significantly faster startup times and reduced memory consumption compared to traditional Java applications.
Use Cases for Quarkus
Quarkus is well-suited for a wide range of use cases, including:
- Microservices: Quarkus is an ideal choice for building microservices architectures due to its fast startup times, low memory footprint, and high performance.
- Serverless Functions: Quarkus can be used to create serverless functions that can be deployed to platforms like AWS Lambda and Azure Functions.
- Cloud-Native Applications: Quarkus is optimized for cloud-native environments, making it a great choice for building applications that are designed to be deployed and managed in the cloud.
- IoT Applications: Quarkus can be used to build IoT applications that require low latency and minimal resource consumption
Advantages of Quarkus Over Spring Boot
While Spring Boot has been a dominant force in the Java ecosystem, Quarkus offers several advantages:
Fast Startup: Quarkus applications start up in milliseconds, making them ideal for microservices architectures where quick response times are crucial.
Performance: Quarkus's AOT compilation and optimized runtime make it significantly faster than Spring Boot applications. This is especially beneficial for microservices architectures where quick response times are crucial.
Memory Footprint: Quarkus applications have a smaller memory footprint compared to Spring Boot applications, making them suitable for resource-constrained environments and cloud-native deployments.
Container-Native Focus: Quarkus is designed to be container-native from the ground up, offering features like live reloading and dev mode for rapid development and testing.
Simplified Configuration: Quarkus uses a convention-over-configuration approach, reducing the amount of boilerplate code required to create applications.
Performance Statistics
Given below is the comparison of few key performance metrics for traditional Java REST based web applications developed using either Spring Boot or Quarkus + JVM or Quarkus + GraalVM (with native executable)
Framework / Runtime |
Average Latency (Time to First Response |
Application with Spring Boot |
4.3 seconds |
Application with Quarkus + JVM |
0.75 seconds |
Application with Quarkus + GraalVM (Native Executable)
|
0.015 seconds |
Framework / Toolkit |
Average Latency (Time to First Response |
Application with Spring Boot |
140 MB |
Application with Quarkus + JVM |
77 MB |
Application with Quarkus + GraalVM (Native Executable)
|
19 MB |
Above results clearly show that Quarkus application which uses native binary executables offers a much better performance with minimal memory footprint.
Quarkus – Why it has faster Startup time?
Consider an application based on frameworks such as Spring and Hibernate. It does following steps while starting an application:
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.
Quarkus – What is AOT (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, an essential component of GraalVM.
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, 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.
Best Practices:
- Sometimes, native executables created by GraalVM do not function properly. This is because GraalVM sometimes over prunes or eliminates the code during the process of dead code elimination. This issue can be addressed by writing NATIVE TESTS in addition to JVM TESTS. Please refer @NativeImageTest offered by Quarkus, to ensure that native executables verify native tests before native binary can be created.
- Quarkus “dev” mode should never be used for production systems, as there is both functional & architectural difference between “dev” and “prod” modes.