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.

Programmer DD
Programmer DD
Programmer DD
Can Virtual Threads Speed Up a Spring Boot Static File Server? A Real‑World Test

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.xml

excerpt:

<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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencySpring BootVirtual Threadsjava-21Static File Server
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

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.