Why Spring Boot 4.0.1’s Virtual Thread Bug Could Kill Your Performance—and How to Fix It

Spring Boot 4.0.1 introduced a critical virtual‑thread configuration bug that disables Jetty’s virtual threads, causing massive CPU waste and slow responses; the article explains the bug’s cause, the official fix in v4.0.1, and provides upgrade steps, workarounds, and monitoring tips.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Why Spring Boot 4.0.1’s Virtual Thread Bug Could Kill Your Performance—and How to Fix It

Spring Boot 4.0 Evolution

Spring Boot 4.0.0 was released in November 2025, built on Spring Framework 7 with modular architecture, JSpecify null‑safety, Java 25 support, and virtual‑thread features. The follow‑up patch 4.0.1 arrived in December 2025, fixing 25 bugs and updating many dependencies.

Virtual Thread Configuration Failure

Bug Symptoms

When spring.threads.virtual.enabled=true is set, Jetty should automatically use virtual threads, promising up to 50× higher concurrency. In practice Jetty continued to use traditional platform threads, leading to:

CPU resources idle (only ~20% utilization while queues grow)

Response time remains around 500 ms instead of the expected 150 ms

Projected cost savings from virtual threads never materialize

Root Cause

The issue stems from the modular refactor of Spring Boot 4.0, where the automatic configuration for Jetty’s virtual‑thread support was misplaced in an else branch and the property binding occurs after Jetty initialization.

Modular architecture split the monolithic JAR into fine‑grained modules; the virtual‑thread initialization logic was incorrectly placed.

In JettyWebServerFactoryCustomizer, the Executor configuration depends on spring.threads.virtual.enabled, but the property is bound too late, so Jetty ignores it.

Lack of integration tests meant the bug only appeared under real high‑concurrency pressure, not in unit tests.

Official Fix in Spring Boot 4.0.1

The Spring Boot team fixed the problem by reordering the initialization sequence and strengthening the conditional checks.

Core Fix Code (pseudo‑code)

// JettyServletWebServerFactory.java – after fix
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    JettyWebServer webServer = new JettyWebServer(prepareServer(), this.resourceLoader, isVirtualThreadsEnabled());
    if (isVirtualThreadsEnabled()) {
        // Inject virtual‑thread executor before server starts
        webServer.setExecutor(createVirtualThreadExecutor());
    }
    return webServer;
}

private boolean isVirtualThreadsEnabled() {
    return this.threadProperties != null &&
           this.threadProperties.getVirtual() != null &&
           this.threadProperties.getVirtual().isEnabled();
}

Fix Strategy Explained

Early property collection – bind spring.threads.virtual.enabled before Jetty creation.

Explicit executor injection – call setExecutor() directly instead of relying on Jetty’s discovery.

Add integration tests – include Gatling load‑test scenarios with 1000+ concurrent requests to verify virtual threads are active.

Developer Recommendations

Upgrade Advice

If you are using any Spring Boot 4.x version, upgrade to 4.0.1 immediately.

# Maven project
./mvnw versions:update-properties -Dincludes=org.springframework.boot:*

# Gradle project
./gradlew dependencyUpdates

Verification Test

@SpringBootTest
class VirtualThreadVerificationTest {
    @Autowired
    private WebServer webServer;

    @Test
    void virtualThreadsShouldBeEnabled() {
        if (webServer instanceof JettyWebServer) {
            Executor executor = ((JettyWebServer) webServer).getServer().getThreadPool();
            assertThat(executor).isInstanceOf(VirtualThreadExecutor.class);
        }
    }
}

Temporary Workaround

When upgrading is not possible, override the Jetty factory to force virtual‑thread executor injection.

@Configuration
public class VirtualThreadWorkaround {
    @Bean
    public JettyServletWebServerFactory jettyFactory(ThreadProperties threadProperties) {
        return new JettyServletWebServerFactory() {
            @Override
            public WebServer getWebServer(ServletContextInitializer... initializers) {
                JettyWebServer server = super.getWebServer(initializers);
                if (threadProperties.getVirtual().isEnabled()) {
                    server.getServer().setExecutor(createVirtualThreadExecutor());
                }
                return server;
            }
            private Executor createVirtualThreadExecutor() {
                return Executors.newVirtualThreadPerTaskExecutor();
            }
        };
    }
}

Pitfalls and Lessons from Major Version Upgrades

Virtual threads are not a silver bullet; the runtime and framework must correctly support them.

Modularization reduces JAR size but increases the risk of missing configuration – full‑stack load testing is essential.

Choose between reactive programming and virtual threads based on workload characteristics.

Observability matters – lack of thread‑pool monitoring delayed detection of this bug.

Monitoring Configuration

Enable actuator endpoints to observe thread usage in production or pre‑release environments.

management:
  endpoints:
    web:
      exposure:
        include: threads,metrics
  endpoint:
    threads:
      enabled: true

Access /actuator/threads to see real‑time distribution of thread types.

Conclusion

Spring Boot 4.0.1’s virtual‑thread configuration fix is a highlight of the first patch release for the 4.x line. It restores the expected 50× concurrency boost on Jetty and reduces response times by about 70 %. The release also marks the end of maintenance for the 3.2‑3.5 series, urging users to migrate to 4.x promptly.

References

Spring Boot 4.0.1 Release Notes:

https://github.com/spring-projects/spring-boot/releases/tag/v4.0.1

Spring Boot 4.0 Migration Guide:

https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide

Spring Boot project page:

https://spring.io/projects/spring-boot
Javabackend developmentSpring BootVirtual ThreadsBug FixJetty
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

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.