I have been testing the Quarkus support we just added in StatLite 0.4.0, a lightweight self-hosted metrics dashboard. StatLite already monitors Spring Boot applications, so I put equivalent Spring Boot and Quarkus applications side by side to exercise both integrations and see how they behaved on a genuinely small server.

The setup was intentionally demanding: both apps got one Star Pulse workload, one JDK 25 build, one Xmx80m profile, and one 512 MiB VM with 256 MiB of swap. One StatLite process monitored both applications and the host.

The short version is not that one framework simply uses more memory everywhere. Quarkus started larger in this workload, then developed a smaller resident working set and a smaller JVM heap under sustained pressure. RSS alone was misleading, because Linux was paging Quarkus more aggressively. In a later follow-up, the machine eventually ran out of memory and swap, and Spring was selected as the OOM victim.

The published experiment record contains the exact JVM flags and versions, selected RSS/PSS/VmSwap measurements, selected checkpoints, kernel/OOM evidence, restart chronology, methodology, and limitations. Full archives, databases, and private metadata are omitted.

Three observations

The numbers below come from three separate observations on that VM. They stay separate on purpose.

Observation What it answers
Health-enabled hour, Sep 1, 2026 Controlled comparison. Both stayed up, zero restarts.
Memory-residency follow-up Why RSS moved, including swap.
Sep 2, 2026 observation Operational failure. Not enough headroom; Spring was OOM-killed.

The setup

Both applications served the same Star Pulse workload. Every five minutes they fetched star, fork, and watcher counts for the same three GitHub repositories and stored the results in H2.

Spring Boot Quarkus
Framework Spring Boot 3.5.5 Quarkus 3.39.1
Runtime JDK 25.0.4 JDK 25.0.4
Heap limit -Xmx80m -Xmx80m
Database H2 H2
Metrics Actuator Prometheus Prometheus/OpenMetrics

The VM was Ubuntu 24.04 with one vCPU. The guest exposed 452 MiB of RAM, with 256 MiB of persistent swap. Both JVMs also used the same 16 MiB initial heap, 256 KiB thread stacks, Serial GC, tiered compilation stopped at level 1, a 32 MiB reserved code cache, and compact object headers:

-Xms16m
-Xmx80m
-Xss256k
-XX:+UseSerialGC
-XX:TieredStopAtLevel=1
-XX:ReservedCodeCacheSize=32m
-XX:+UseCompactObjectHeaders

This is a JVM-mode comparison, not a native-image comparison. It is also independent of the earlier single-application 512 MB and 256 MB JDK 25 runs.

Quarkus started larger

At the settled checkpoint in the memory-residency follow-up, Spring Boot was about 112 MiB RSS and Quarkus was about 145 MiB. That was the first surprise: the framework commonly associated with small deployments had the larger resident process at startup.

Startup memory and resident memory after sustained pressure are different measurements. The rest of the hour is where the picture changed.

The relationship reversed under pressure

The supporting memory-residency follow-up recorded the same processes at the settled checkpoint and after an hour:

Framework Settled RSS 60-minute RSS 60-minute JVM heap used 60-minute swap residency
Spring Boot ~112 MiB ~154 MiB ~50.3 MB ~66 MiB
Quarkus ~145 MiB ~83 MiB ~33.1 MB ~91 MiB

Quarkus RSS fell substantially, but its heap did not fall with it. Heap grew from about 28.0 MB to 33.1 MB while swapped memory grew from about 29 MiB to 91 MiB. Linux paging therefore explains an important part of the apparent RSS advantage.

Even with that qualification, Quarkus had the smaller heap and the smaller resident working set at the relevant comparison point. Spring moved in the opposite direction, with a larger later RSS and heap in this workload.

This is a workload-specific observation on an extremely constrained VM. It is not a claim that Quarkus always uses 83 MiB or that Spring Boot always uses 154 MiB.

The health-enabled hour finished with zero restarts and the opposite RSS ranking: about 61 MiB for Spring and 156 MiB for Quarkus. That is why the two runs stay separate. RSS alone is not a stable ranking on this machine.

The 512 MB limit

The VM first exhausted its tight memory and swap margin during later Ubuntu package maintenance. Spring was OOM-killed, and the package restart handling also restarted the application services. That demonstrated how little headroom the machine had for routine operating-system work.

The stronger result came afterward. Once the obvious package-maintenance pressure had cleared, Spring and Quarkus were started again with the same JDK 25 and Xmx80m configuration. A fresh one-hour observation began. About 41 minutes later, swap was again effectively exhausted and the kernel OOM-killed Spring. Spring restarted automatically. Quarkus and StatLite did not restart, and the final health checks were successful.

The later OOM was not caused by the active apt transaction seen earlier. It also should not be read as proof that no other operating-system activity contributed to the final memory state. The defensible conclusion is that 512 MiB of RAM and 256 MiB of swap did not provide enough overall headroom to guarantee reliable survival of both JVM applications for an hour.

StatLite stayed small and kept watching

StatLite remained a small part of the system, at roughly 10 to 15 MiB RSS in the relevant checkpoints. It continued monitoring through the Spring restart, recorded the temporary health failure, and observed recovery. Quarkus and StatLite stayed available while Spring was being restarted.

The application workload also encountered unauthenticated GitHub rate limits. Those upstream 403 responses are documented in the experiment record. They affected application polling, but they were not StatLite or framework crashes.

The 24-hour views below are context through the morning maintenance window, not the timed comparison. Runtime memory MB is JVM heap, not RSS. The current cards come from a later local replay.

StatLite combines lightweight application monitoring with basic host visibility in one small self-hosted binary. If this looks useful for your servers, star it on GitHub. You can also try it with Spring Boot or Quarkus in a few minutes.

⭐ Star StatLite on GitHub Try StatLite

Takeaways

  • Quarkus started with higher RSS, so startup memory alone gave a misleading picture.
  • Under sustained memory pressure in this workload, Quarkus developed a smaller resident working set and smaller heap than Spring Boot.
  • The 512 MiB RAM / 256 MiB swap configuration was too tight for reliable operation of both JVMs. Spring was eventually OOM-killed in a subsequent observation after package-maintenance pressure had cleared.
  • StatLite remained small and continued monitoring through the failures.

This is useful evidence about behavior under extreme memory pressure, not a clean definitive framework-memory benchmark. At this level of pressure, Linux paging and OOM behavior become part of the result. A higher-headroom follow-up on a less constrained VM would reduce those side effects and provide a cleaner comparison of the frameworks themselves. That is a future experiment, not a result reported here.

Evidence and reproducibility

The method, JVM flags, selected checkpoints, and kernel evidence are in the public experiment record. Full archives, databases, and private metadata are omitted. Star Pulse for both frameworks is included in that tree. StatLite is the monitoring dashboard shown in the screenshots: a single Go binary using SQLite, with Spring Boot Actuator and Quarkus metrics collectors.

Watching two JVMs on a box this small is exactly the kind of job StatLite is for.

⭐ Star StatLite on GitHub Public experiment