How Does The Quarkus Java Framework Compare With Spring ?

Spring vs Quarkus
Learn the differences between the Quarkus framework and the Spring framework: features / library compatibility, the startup time, the first response time, the memory footprint and GraalVM native image support.

Feature comparison

Both Quarkus and Spring offer a comprehensive stack of technologies and tools to build modern Java applications.

While Quarkus is more in line with Java EE standards such as CDI and JAX-RS, Spring provides an alternative modular architecture that revolves around it's core container.

Feature Quarkus Spring
Build time class initialization yes no
Kubernetes resources generation yes no
GraalVM native images support yes limited
Dependency injection & components management CDI, Spring DI extension Spring Core
Web / REST API development JAX-RS, Spring Web extension Spring MVC, JAX-RS
Reactive / non-blocking web stack Vert.x Spring WebFlux, Vert.x
REST Clients MicroProfile Rest Client RestTemplate, WebClient, Feign
JSON serialization JSON-B, Jackson Jackson, JSON-B
Simplified data access Panache, Spring Data JPA extension Spring Data: JPA, JDBC, MongoDB, LDAP, KeyValue
Reactive data access Reactive SQL Clients, Reactive MongoDB Client Spring Data R2DBC
Hibernate support yes yes
Authentication & authorization Elytron Security Spring Security
Application monitoring MicroProfile Health, MicroProfile Metrics Spring Boot Actuator
Resilience & Fault tolerance MicroProfile Fault Tolerance Netflix Hystrix
Kotlin support yes yes
Online project starter https://code.quarkus.io/ https://start.spring.io/

References

Performance overview of Quarkus vs Spring Boot

The illustrations of this section show the startup time and heap usage after GC of a Quarkus app with RESTEasy and a Spring Boot Web app. Both apps expose one simple endpoint. JDK 1.8.0_201 was used to perform the tests.

Startup time & memory footprint

In this example, Quarkus is 2 times faster to start and consumes half the heap size used by Spring.

Because part of the initialization is done at build time, Quarkus has a reduced startup time.

The small memory footprint of Quarkus results from the fact that the application will only contain the classes that are actually needed at runtime.

Time to first request

To measure more accurately how long a framework needs to start, I wrote a simple node.js script to calculate the first response time to API requests after starting up Quarkus and Spring Boot.

const spawn = require("child_process").spawn;
const request = require("request");

const pingIntervalMs = 100;

const args = process.argv.slice(2);
if (args.length == 0) {
  console.log('The path of java executable jar must be specified !');
}

const proc = spawn("java", ["-jar", args[0]], {
  timeout: 60000
});

const startTime = new Date().getTime();
const intervalHandle = setInterval(() => {
  request("http://localhost:8080/hello", (error, response, body) => {
      if (!error && response && response.statusCode === 200 && body) {
          const time = new Date().getTime() - startTime;
          console.log(time + " ms");
          clearInterval(intervalHandle);
          proc.kill();
          process.exit(0);
      }
    }
  );
}, pingIntervalMs);

I launched the script 5 times with a Quarkus runnable jar and the same times with a Spring Boot uber jar (the same jars used in the previous section).

The results were in accordance with the previously reported startup times.

First response time - Quarkus

First response time - Spring Boot

On average, Quarkus took 1862 milliseconds to respond to the first request, while Spring Boot took 3580 milliseconds.

GraalVM native performance

Quarkus is an order of magnitude faster to start when packaged as a GraalVM native image and uses a much smaller heap.

This article can give you an idea of the startup time and memory footprint of frameworks that support native images such as Quarkus, Micronaut and Helidon.


Quarkus startup time
Quarkus startup time
Spring startup time
Spring startup time
Quarkus heap memory usage
Quarkus heap memory usage
Spring heap memory usage
Spring heap memory usage

GraalVM native images support

The Spring Framework only provides experimental support for GraalVM native images as of 5.1. A more mature solution will be provided starting from the 5.3 version (Q2 2020).

In the other hand, Quarkus already provides out of the box support for GraalVM native images, and makes it easy to circumvent the limitations of Java native images by applying the following adaptations:

Reflection

By default, the GraalVM removes all the classes/methods/fields that are not used directly in a call tree. For this reason, it is necessary to manually register the classes that will be manipulated using reflection (such as model/domain classes that require serialization using frameworks that use reflection) either by annotating them with @RegisterForReflection or using a configuration file when the class cannot be modified (third-party library for example).

Some classes will not be required to be registered for reflection when Quarkus can automatically deduce them at build time by analyzing the code (REST methods for example).

Class initialization

All classes are initialized by default at build time by Quarkus. When a class initialization must be done at runtime, the --initialize-at-run-time=<package or class> build argument can be used.

Proxy Classes

Proxy classes must be defined at image build time by specifying the list of interfaces that they implement using the build argument -H:DynamicProxyConfigurationResources=<comma-separated-config-resources>

Including resources

Only the web resources found in META-INF/resources will be included by default in the native executable. To bundle more resources into the native executable, a resources-config.json configuration file can be used.