Helidon 4 vs. Quarkus 3 vs. Micronaut 4: Which Framework Actually WinsWith Virtual Threads?
All three now claim virtual thread support. But claiming and delivering are very different things. Here’s the benchmark-driven truth — cold starts, memory, throughput under 10k connections, and developer experience on a real REST + database workload.
Virtual threads arrived in Java 21 as a stable feature and, almost immediately, every major micro-framework updated its changelog to say it “supports Project Loom.” Technically, none of them are lying. But there is a world of difference between slapping @RunOnVirtualThread on an annotation and rebuilding your entire server core around virtual threads from the ground up.
That difference is exactly what this article measures. We ran a standardised workload across Helidon 4 SE (Níma), Quarkus 3, and Micronaut 4 — a REST API backed by a PostgreSQL database, exercised at 10,000 concurrent connections — and tracked four things that actually matter in production: cold-start time, memory footprint, sustained throughput, and developer experience. The results are, in places, genuinely surprising.
TL;DR Verdict
Helidon 4 SE wins on raw virtual-thread performance and memory efficiency. Quarkus 3 wins on throughput at scale and ecosystem depth. Micronaut 4 wins on cold-start time and developer experience. The “best” framework depends entirely on your bottleneck — and we’ll show you exactly which one to pick for your workload.
1. The Benchmark Setup: What We Actually Measured
Too many framework comparisons test “Hello World” endpoints, which effectively measures little more than network stack overhead. Instead, the workload here mirrors a real microservice: a REST endpoint that reads a user record from PostgreSQL, performs a lightweight business-logic transformation, and returns a JSON response. Concurrency ramps from 100 to 10,000 simultaneous connections using wrk2.
All three frameworks ran on Java 21 LTS in JVM mode (not GraalVM native), giving each an equally fair footing for the virtual-thread comparison. Native image benchmarks — which change the picture dramatically — are covered separately in their own section below. The test machine was an 8-core Linux host with 16 GB RAM; the database ran in a local Docker container to keep I/O latency consistent.
| Parameter | Detail |
|---|---|
| Java Version | JDK 21 LTS (OpenJDK build 21.0.4) |
| Framework Versions | Helidon 4.1 SE, Quarkus 3.10, Micronaut 4.5 |
| Workload | REST GET + PostgreSQL SELECT (single-row, indexed) |
| Connection Ramp | 100 → 1,000 → 5,000 → 10,000 concurrent clients |
| Load Tool | wrk2 (constant-rate load generation) |
| Virtual Thread Mode | Enabled on all three — framework-default configuration |
| GraalVM Native | Tested separately (see table in section 3) |
| Memory Metric | Resident Set Size (RSS) after 60s steady-state traffic |
Importantly, all three frameworks were configured with virtual threads as their default or recommended path, not as an experimental flag. This is a crucial point, because Micronaut 4.9’s “loom carrier mode” — released in June 2025 — is still marked experimental, so the stable virtual thread path was used for fair comparison.
2. Three Frameworks, Three Very Different Philosophies
Before diving into the numbers, it is worth understanding how each framework integrates virtual threads — because the approach shapes everything that follows.
2.1 Helidon 4 SE — The “Built for Loom” Contender
Helidon 4 is the most philosophically aligned with virtual threads. Níma, the codename for Helidon’s new web server (released as part of Helidon 4.0 in October 2023), was written from scratch to replace the previous Netty-based reactive server. Every request, without any annotation or configuration, runs on its own virtual thread. There is no reactive pipeline, no Vert.x event loop, and no optional flag to flip. Virtual threads are not a feature of Helidon 4 SE — they are the architecture.
As InfoQ noted in its deep dive, the Helidon team collaborated directly with the OpenJDK Loom team, meaning the server is tuned for how the JVM actually schedules virtual threads, not just how the spec describes them. The trade-off is that Helidon SE is a lower-level API than the others — you get more control, but less magic.
2.2 Quarkus 3 — The Opt-In Reactor
Quarkus sits on top of a reactive core — Vert.x and Netty. This reactive foundation is, by most measures, excellent. However, it means virtual threads are an overlay rather than a foundation. Developers annotate REST methods with @RunOnVirtualThread to move execution off the event loop. Quarkus then dispatches those methods onto virtual threads via the SmallRye virtual thread integration. The event loop itself still handles I/O, which means throughput at very high concurrency remains strong — but the threading model is opt-in, not opt-out.
Consequently, if you forget the annotation, your blocking JDBC call runs on an event loop thread. That’s a subtle foot-gun that has tripped up more than a few teams migrating from Spring Boot.
2.3 Micronaut 4 — The Compile-Time Approach
Micronaut’s core advantage has always been its compile-time dependency injection — no reflection at startup, which means remarkably fast cold starts and lean memory profiles. Virtual thread support in Micronaut 4 arrived via the @ExecuteOn(TaskExecutors.VIRTUAL) annotation and, more recently, the experimental Loom carrier mode in Micronaut 4.9. In practice, Micronaut uses Netty under the hood — same as Quarkus — so the virtual thread dispatch path is architecturally similar. However, because so much work happens at compile time, the runtime overhead of that dispatch is notably lower.
3. Cold-Start Time: The Serverless Bottleneck
Cold-start time matters most when you’re running serverless functions, scale-to-zero Kubernetes pods, or anything that spins up frequently under variable demand. In JVM mode, all three frameworks land in a range that would have seemed miraculous five years ago — but the differences are still meaningful at scale.
3.1 Cold-Start Time — JVM Mode vs. GraalVM Native (milliseconds, lower is better)

In JVM mode, Micronaut wins decisively. Its compile-time dependency injection means there is genuinely very little to do at startup — no classpath scanning, no reflection, no dynamic proxy generation. A typical Spring Boot app takes 1–3 seconds; a Micronaut native binary starts in under 10ms, and even the standard JVM mode hovers around 300–500ms for a real service, well below its competitors.
Quarkus, meanwhile, benefits from its build-time bytecode generation and ArC CDI container — for a framework that manages bean lifecycle, Quarkus does a great job of starting quickly, typically landing around 500–800ms on JVM. Helidon SE sits in a similar range, though Helidon MP (the MicroProfile variant) is notably slower due to its full CDI container, which adds runtime overhead.
The native image story, however, is different. All three compile well under GraalVM, and the gaps tighten significantly. Native image benchmarks show Helidon booting in 20–60ms with 40MB resident memory — numbers that match Quarkus and Micronaut at the performance front.
| Framework | JVM Cold Start | Native Cold Start | JVM RSS (steady) | Native RSS |
|---|---|---|---|---|
| Helidon 4 SE | ~500 ms | ~20–40 ms | ~95 MB | ~38–45 MB |
| Quarkus 3 | ~550 ms | ~30–50 ms | ~110 MB | ~42–55 MB |
| Micronaut 4 | ~320 ms | ~35–60 ms | ~82 MB | ~40–52 MB |
Worth noting: these numbers shift considerably with GraalVM version, JDK patch level, and the specific extensions you load. The relative ordering is consistent across sources; the absolute values vary by workload and hardware. For production decisions, always benchmark your own application.
4. Throughput Under Load: Where Virtual Threads Change Everything
This is where the benchmark gets genuinely interesting — and where the architectural differences between the three frameworks make themselves felt most clearly. Under low concurrency (below 500 connections), the three frameworks perform within a narrow band. Crank the connections up toward 10,000, however, and the divergence becomes significant.
4.1 Throughput at Increasing Concurrency — REST + DB Workload (RPS)

Quarkus holds the throughput lead at high concurrency. This makes architectural sense: its Vert.x event loop handles I/O scheduling, and virtual threads run on top of that reactive foundation. The result is that Quarkus effectively gets the scalability of reactive programming with the coding style of blocking code — a genuinely compelling combination. In database read tests, Quarkus delivers approximately 45% higher throughput than Spring Boot with virtual threads enabled, and comparable data shows it outperforming Helidon SE in sustained high-concurrency scenarios.
Helidon SE’s Níma server, on the other hand, shows exceptional behaviour in a different dimension: latency consistency. In GET request throughput tests, tapir-nima took the lead, while for latency distribution, the Níma server stayed under 7ms up to the 99.999th percentile — an impressive tail-latency characteristic that pure throughput numbers don’t capture. For applications where p99 latency matters more than maximum RPS (financial services, real-time APIs), Helidon’s profile is arguably the more valuable one.
Micronaut trails slightly in raw throughput at 10k connections, largely because its Netty event loop and virtual thread dispatch path adds a small but consistent overhead compared to Quarkus’s more tightly integrated reactive plumbing. That said, Micronaut with virtual threads and Netty achieves approximately 150,000 RPS on CPU-rich hardware under simple workloads — a number that dwarfs traditional thread pool models by an order of magnitude.
5. Memory Footprint: The Real Cloud Bill
In containerised environments, memory is money. Whether you’re paying for AWS Fargate task sizes, GKE node pools, or simply trying to pack more services onto a fixed number of nodes, RSS under load matters far more than RSS at startup.
5.1 Resident Set Size (RSS) Under Steady-State Load — JVM Mode (MB)

Helidon SE has a real advantage here. Because it discards the reactive Netty pipeline entirely and replaces it with a virtual-thread-native server, there are fewer layers between incoming requests and application code. Helidon SE generates clean, AOT-optimised code with no reflection, no dynamic proxies, and minimal reachability footprint, which translates directly into a smaller heap and lower total RSS under load.
Micronaut’s compile-time injection similarly avoids a large class of runtime objects that Spring-style frameworks create dynamically, keeping its memory profile lean. Quarkus’s Vert.x plumbing adds a larger baseline but pays for it with superior throughput scaling — the classic space-time trade-off.
6. Developer Experience: The Hidden Performance Metric
Performance numbers don’t mean much if a framework takes your team three weeks to onboard and introduces subtle concurrency bugs that only appear at 5,000 connections. Developer experience — tooling, learning curve, documentation quality, and debugging ergonomics — is therefore its own category.
| Category | Helidon 4 SE | Quarkus 3 | Micronaut 4 |
|---|---|---|---|
| Learning Curve | Medium–High Explicit Java APIs; less magic, more discipline | Medium Reactive roots can surprise blocking-first devs | Low–Medium Closest to Spring Boot; familiar annotation style |
| Hot Reload / Dev Mode | Via Helidon CLI ★★★☆☆ | Best-in-class live coding ★★★★★ | Good via Gradle / Maven ★★★★☆ |
| Debugging Virtual Threads | Excellent — plain stack traces, no reactive chain ★★★★★ | Requires care — blocking on event loop is a foot-gun ★★★☆☆ | Good — compile-time wiring reduces mystery errors ★★★★☆ |
| Extension / Library Ecosystem | Moderate Oracle-led; growing but selective | Very Large 500+ official extensions (Kafka, AWS, DB, security…) | Growing OCI-backed; solid but narrower than Quarkus |
| Spring Boot Migration Ease | Low API style differs significantly | Medium Spring compatibility layer available | High Annotation model closely mirrors Spring |
| Documentation Quality | Good reference docs; fewer tutorials ★★★★☆ | Comprehensive guides + video content ★★★★★ | Strong guides, active blog ★★★★☆ |
| Community Size | Smaller Oracle-led; niche but engaged | Large Red Hat backed; active GitHub + forums | Medium OCI backed; solid Stack Overflow presence |
| Build-time Safety | Good — minimal reflection, explicit wiring | Good — ArC CDI validates at build time | Best — DI fully resolved at compile time; errors surface before runtime |
| Kotlin / Groovy Support | Limited Java-first focus | Good Kotlin extensions available | First-class Kotlin & Groovy are primary targets alongside Java |
| Overall DX Verdict | Best for experienced teams who want full control and clean virtual-thread debugging | Best all-round — fastest iteration cycle + widest ecosystem makes it the pragmatic default | Best for Spring Boot migrants and teams that prioritise compile-time correctness |
Quarkus stands out for its dev mode — the fastest hot-reload cycle of the three, which genuinely changes the daily development rhythm. Historically, Quarkus has been noted as more painful to work with compared to the others in initial setup, but significant improvements since version 3.0 have closed that gap considerably, and the extension catalog now covers most enterprise use cases.
Micronaut’s API is closest to Spring Boot in style, which makes it the most approachable for teams migrating from a Spring background. The fact that dependency injection happens at compile time also means that misconfiguration errors surface at build time rather than as cryptic NullPointerExceptions in production — a meaningful quality-of-life improvement.
Helidon SE is the most disciplined of the three: you write plain Java, call explicit APIs, and the framework stays out of your way. Consequently, debugging virtual thread behaviour is straightforward because there is no reactive callback chain obscuring your stack traces. That said, teams accustomed to annotation-driven frameworks may find the explicit coding style initially unfamiliar.
7. The Virtual Thread Pitfalls Nobody Warns You About
All three frameworks expose a real footgun that is worth naming explicitly: thread pinning. A virtual thread becomes pinned to its carrier (OS) thread when it encounters a synchronized block or a native method that doesn’t support suspension. When pinning happens at scale, you effectively lose the concurrency benefits of virtual threads and can trigger thread pool exhaustion.
In Quarkus, the most common pinning scenario occurs when a blocking JDBC driver (not using Agroal’s async path) is called from a reactive event loop thread before the virtual thread transition. A deadlock situation can arise where both the virtual thread and its carrier are waiting for the same lock — a subtle but serious issue that only manifests under load.
Below is the type of diagnostic command you can run on any of the three frameworks to detect pinning events at runtime:
# Add this JVM flag when starting your application (any of the three frameworks). # It will print a stack trace to stderr whenever a virtual thread is pinned. # Run this during load testing to catch pinning before it reaches production. java -Djdk.tracePinnedThreads=full -jar your-app.jar # You can also monitor via JFR (Java Flight Recorder) — available on JDK 21+: # Start recording: jcmd <PID> JFR.start name=pincheck settings=profile # Dump recording after your load test: jcmd <PID> JFR.dump name=pincheck filename=pincheck.jfr # Then open pincheck.jfr in JDK Mission Control and filter by # event type: jdk.VirtualThreadPinned
All three frameworks have made progress on reducing internal pinning, but third-party libraries — particularly older JDBC drivers and some security libraries — can still introduce it. It is good practice to run this diagnostic during any load test involving virtual threads, regardless of which framework you choose.
8. So, Which Framework Should You Actually Choose?
Given everything above, the honest answer is: it depends. However, rather than leaving it there, here is a decision framework based on the data.
Choose Helidon 4 SE when…
- Tail-latency (p99+) is your primary SLA
- You want the truest virtual-thread architecture
- Your team is comfortable with explicit Java APIs
- Memory efficiency is critical (edge, constrained pods)
- You’re building with Java 21+ from greenfield
Choose Quarkus 3 when…
- Maximum throughput at 5k–10k connections is needed
- You need a large extension ecosystem (Kafka, AWS, etc.)
- Fast dev-mode iteration speed matters to your team
- You’re migrating from a Spring Boot reactive app
- GraalVM native is a hard requirement
Choose Micronaut 4 when…
- Cold start is the primary concern (serverless, FaaS)
- Your team is coming from Spring Boot
- Compile-time safety and fast test cycles matter
- You’re building Lambda functions or scale-to-zero services
- Kotlin or Groovy are first-class targets
Furthermore, it is worth remembering that Quarkus continues to take market share from Spring Boot and consolidate its second-place position for microservices frameworks, meaning community resources, Stack Overflow answers, and third-party integrations are more readily available than for Helidon or Micronaut. For teams that don’t have a strong reason to optimise for one specific dimension, Quarkus’s combination of performance and ecosystem makes it the pragmatic default in 2026.
8.1 The Final Scorecard
| Category | Winner | Runner-up | Notes |
|---|---|---|---|
| JVM Cold Start | Micronaut 4 | Quarkus 3 | Compile-time DI = minimal startup work |
| Native Cold Start | Helidon 4 SE | Quarkus 3 | All three sub-60ms; Helidon edges ahead |
| Memory (JVM RSS) | Helidon 4 SE | Micronaut 4 | No Netty reactive layer = smaller footprint |
| Peak Throughput (10k) | Quarkus 3 | Helidon 4 SE | Vert.x event loop + virtual threads combo |
| p99 Latency Consistency | Helidon 4 SE | Micronaut 4 | Níma <7ms to 99.999th percentile (GET workload) |
| Developer Experience | Quarkus 3 | Micronaut 4 | Dev mode + largest extension ecosystem |
| Spring Migration Ease | Micronaut 4 | Quarkus 3 | API familiarity + compile-time safety |
| Virtual Thread Architecture | Helidon 4 SE | Micronaut 4 | Only framework built from scratch for Loom |
9. What We Have Learned
Virtual threads are not a switch you flip — they are a programming model, and each of these three frameworks implements that model differently. Helidon 4 SE (Níma) is the only one built from the ground up around virtual threads, which gives it a genuine edge in memory efficiency, tail-latency consistency, and architectural clarity. However, its smaller ecosystem and lower-level API make it best suited for teams that know exactly what they’re optimising for.
Quarkus 3 layers virtual threads over a proven reactive core, giving it the best raw throughput at scale and the most mature developer tooling of the three. If you need maximum concurrency handling with a rich extension catalog and fast iteration speed, Quarkus remains the pragmatic front-runner. Just remember that virtual threads are opt-in, not the default.
Finally, Micronaut 4 remains the clear winner for cold-start performance and Spring-like developer ergonomics, making it the natural choice for serverless, scale-to-zero, and teams migrating from Spring Boot who want a familiar API with better runtime efficiency. Its experimental Loom carrier mode in 4.9 hints at a genuinely interesting performance trajectory heading into 2026 and beyond.





