Backend Development 14 min read

Comparative Performance Analysis of Spring Boot and Quarkus for Cloud‑Native Java Microservices

This article compares Spring Boot and Quarkus by describing their architectures, presenting a reactive test application that uses PostgreSQL, detailing a JMeter performance test plan, and analyzing results such as startup time, build size, CPU, memory, and response latency to help developers decide when to migrate from Spring Boot to Quarkus.

Java Captain
Java Captain
Java Captain
Comparative Performance Analysis of Spring Boot and Quarkus for Cloud‑Native Java Microservices

Spring Boot is a well‑known Java framework for building enterprise applications, while Quarkus is a newer, Kubernetes‑native Java framework marketed as “Supersonic Subatomic Java” and optimized for OpenJDK HotSpot and GraalVM.

Quarkus offers faster startup, lower memory usage, and the ability to compile native images, making it attractive for containerized and serverless environments.

The article presents a simple comparison of the two frameworks, explaining their differences and similarities, and describes a set of performance tests using two fully reactive applications (Spring WebFlux and Quarkus reactive) that expose three REST APIs for creating, querying, and searching postal codes backed by PostgreSQL.

A test plan is defined: JMeter runs a 5‑minute load test, warming up the services, then ramping up to 1,500 concurrent users while VisualVM monitors resource usage.

All tests are executed on a specified hardware configuration, acknowledging limited isolation from other processes.

Results show that Quarkus’s JVM and native builds start roughly twice as fast as Spring Boot, with build times of 20 s vs 39 s (JVM) and 9 min vs 13 min (native). Artifact sizes are also smaller (75 MB vs 109 MB native, 4 KB vs 26 MB JVM).

CPU usage indicates higher consumption during the warm‑up phase for JVM builds, after which usage stabilizes across all variants.

Memory analysis reveals that Quarkus reserves less heap from the start and consumes less memory overall, although native versions recycle memory less efficiently under default GC settings.

Response‑time measurements suggest Spring Boot has a slight advantage in latency and thread efficiency, especially in JVM mode, while Quarkus native images excel in low‑resource scenarios.

The overall conclusion is that both frameworks are viable: native builds are ideal for short‑lived, resource‑constrained workloads, whereas JVM builds provide stable, high‑throughput performance for long‑running services.

The article also discusses migration from Spring Boot to Quarkus, highlighting Spring API compatibility (DI, Web, Data JPA) and providing code examples that can be found on GitHub.

Example Spring controller:

import java.util.List;
import java.util.Optional;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/person")
public class PersonController {
    @GetMapping(path = "/greet/{id}", produces = "text/plain")
    public String greetPerson(@PathVariable(name = "id") long id) {
        String name = "";
        // ...
        return name;
    }

    @GetMapping(produces = "application/json")
    public Iterable
findAll() {
        return personRepository.findAll();
    }
}

Quarkus Spring repository example:

package org.acme.springmp;

import java.util.List;
import org.springframework.data.repository.CrudRepository;

public interface PersonRepository extends CrudRepository
{
    List
findByAge(int age);
}

Quarkus service with MicroProfile fault tolerance:

import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class PersonService {
    @Autowired
    @RestClient
    SalutationMicroProfileRestClient salutationRestClient;

    @Value("${fallbackSalutation}")
    String fallbackSalutation;

    @CircuitBreaker(delay=5000, failureRatio=.5)
    @Fallback(fallbackMethod = "salutationFallback")
    public String getSalutation() {
        return salutationRestClient.getSalutation();
    }
}
JavamicroservicesKubernetesperformance testingSpring BootQuarkusNative Images
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.