Can Virtual Threads Speed Up a Spring Boot Static File Server? A Real‑World Test
This article evaluates the performance of Spring Boot 3.2's virtual threads versus traditional platform threads when serving static files, using a MacBook Pro M2, Bombardier load testing, and detailed latency and throughput metrics across multiple concurrency levels.
Previously we shared several articles on Java 21 and Spring Boot 3.2 virtual threads, and many developers aim to leverage virtual threads to improve program performance. This article examines which scenarios benefit from virtual threads by testing a static file server.
Spring Boot 3.2, released in November 2023, introduced several groundbreaking features:
Virtual threads: use Project Loom to improve scalability and reduce resource consumption.
Native Image support: compile to fast‑starting native executables.
JVM checkpoints (CRaC): enable rapid application restarts.
RestClient: simplify HTTP interactions.
Spring for Apache Pulsar: integrate powerful messaging.
Virtual threads are lightweight threads that reduce the effort required to write, maintain, and debug high‑throughput concurrent applications. Unlike platform threads, which are thin wrappers around OS threads and limited by the number of OS threads, virtual threads are not bound to a specific OS thread; they suspend on blocking I/O, allowing the underlying OS thread to serve other virtual threads. They excel in I/O‑bound tasks but are not suited for long‑running CPU‑intensive work.
This series explores virtual threads across use cases—from a simple "hello world" to static file serving (I/O‑bound), QR‑code generation (CPU‑bound), and multipart/form data handling (mixed workload). The previous article showed negligible performance differences in a trivial hello‑world test. Here we focus on a realistic static file server scenario.
Test Environment
All tests ran on a MacBook Pro M2 with 16 GB RAM, 8 physical cores and 4 efficiency cores. The load generator was Bombardier, a fast HTTP benchmarking tool written in Go.
Software versions:
Java v21.0.1
Spring Boot 3.2.1
Program Configuration
No additional Java files are required; the static file server works purely via configuration. application.properties content:
server.port=3000
spring.mvc.static-path-pattern=/static/**
spring.web.resources.static-locations=file:/Users/mayankc/Work/source/perfComparisons/static/Enable virtual threads by adding:
spring.threads.virtual.enabled=true pom.xmlexcerpt:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1‑SNAPSHOT</version>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>Test Data
One hundred thousand 100 KB files were placed in the static resources directory (each exactly 102 400 bytes) with names ranging from 1 to 100 000.
Bombardier generated random request URLs such as http://localhost:3000/static/<file‑name>.
Application Scenario
Each test began with a 5 K request warm‑up phase to stabilize results.
Measurements were taken at concurrency levels of 50, 100, and 300, each handling a workload of five million requests.
Result Evaluation
Beyond raw throughput, we captured latency distribution (min, percentiles, max) and requests per second, while also monitoring CPU and memory usage for a comprehensive performance picture.
Test Results
Results are presented in the following charts:
Conclusion
The analysis of the static file service shows that platform (physical) threads performed slightly better in both speed and resource efficiency, contrary to our expectations.
This I/O‑limited scenario may not fully showcase the advantages of virtual threads; database‑driven workloads could reveal more compelling benefits. The load might have been insufficient for virtual threads to demonstrate their maximum potential. Future articles will explore URL short‑ener (database‑bound), QR‑code generation (CPU‑bound), and mixed‑workload cases such as multipart form handling to uncover where virtual threads truly excel.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
