Why Big Companies Replace SpringBoot's Default Tomcat with Undertow – Performance Secrets Revealed

This article explains why many large enterprises forbid the default Tomcat in SpringBoot projects, comparing memory usage, concurrency handling, architectural differences, and configuration flexibility of Tomcat and Undertow, and provides migration guidelines and real‑world case studies.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Why Big Companies Replace SpringBoot's Default Tomcat with Undertow – Performance Secrets Revealed

Introduction

Many large companies now prohibit using SpringBoot's default embedded Tomcat and instead require Undertow because of its superior performance.

1. SpringBoot Default Choice and Current Situation

SpringBoot, the most popular Java framework, bundles Tomcat as the default web container, leading developers to adopt a "out‑of‑the‑box" approach, while enterprises are shifting to Undertow in production.

2. Performance Comparison

2.1 Memory Usage Comparison

Under identical conditions, the following memory metrics were observed:

Tomcat: startup memory 120 MB, heap 80 MB, non‑heap 25 MB, thread memory 15 MB.

Undertow: startup memory 85 MB, heap 60 MB, non‑heap 15 MB, thread memory 10 MB.

Optimisation ratios: startup ‑29 %, heap ‑25 %, non‑heap ‑40 %, thread ‑33 %.

Undertow shows a clear advantage in memory consumption, which translates into significant cost savings for large‑scale microservice deployments.

2.2 Concurrency Handling

In a concurrency test, Undertow also performed better:

// Performance test example
@SpringBootTest
class WebContainerPerformanceTest {
    @Test
    void testConcurrentPerformance() {
        // Simulate 1000 concurrent users for 30 seconds
        LoadTest loadTest = LoadTest.configure()
            .threads(1000)
            .duration(30, TimeUnit.SECONDS)
            .build();
        TomcatResult tomcatResult = loadTest.runWithTomcat();
        UndertowResult undertowResult = loadTest.runWithUndertow();
        System.out.println("QPS - Tomcat: " + tomcatResult.getQps());
        System.out.println("QPS - Undertow: " + undertowResult.getQps());
        System.out.println("Avg response time - Tomcat: " + tomcatResult.getAvgResponseTime());
        System.out.println("Avg response time - Undertow: " + undertowResult.getAvgResponseTime());
    }
}

Typical results:

Tomcat : QPS 8500, average response time 15 ms

Undertow : QPS 12000, average response time 8 ms

3. Underlying Architecture Differences

3.1 Tomcat Architecture

Tomcat uses a traditional BIO/NIO connector model, which is relatively heavyweight and introduces additional overhead.

3.2 Undertow Architecture

Undertow is built on the modern XNIO foundation and features:

IO thread and worker thread separation : IO threads handle network IO, worker threads handle business logic.

Event‑driven architecture : callback‑based event handling.

Zero‑copy capability : direct buffers reduce memory copying.

4. Memory Management

4.1 Direct Memory Usage

// Undertow memory management example
public class UndertowMemoryManagement {
    // Use direct buffer to handle requests
    public void handleRequest(HttpServerExchange exchange) {
        ByteBuffer buffer = exchange.getConnection().getBufferPool().allocate();
        try {
            readRequestData(exchange, buffer);
            processRequest(buffer);
            writeResponse(exchange, buffer);
        } finally {
            exchange.getConnection().getBufferPool().free(buffer);
        }
    }
    // Tomcat typically requires multiple memory copies
    public void tomcatHandleRequest(Request request, Response response) {
        byte[] inputData = readInputStream(request.getInputStream());
        byte[] outputData = processData(inputData);
        response.getOutputStream().write(outputData);
    }
}

4.2 Connection Pool Optimization

# Undertow configuration example
server:
  undertow:
    # IO threads (usually CPU cores)
    io-threads: 8
    # Worker threads (adjust per business)
    worker-threads: 200
    direct-buffers: true
    buffer-size: 16384
    max-connections: 10000
    max-http-post-size: 10485760
    no-request-timeout: 60000
    drain-wait-time: 20000

Compared with Tomcat's configuration:

# Tomcat configuration example
server:
  tomcat:
    max-connections: 10000
    max-threads: 200
    min-spare-threads: 10
    max-http-post-size: 10485760
    connection-timeout: 20000

5. Concurrency Model

5.1 Undertow XNIO Architecture

// XNIO worker model example
public class XNIOWorkerModel {
    public void demonstrateWorkerModel() {
        XnioWorker worker = Xnio.getInstance().createWorker(
            OptionMap.create(Options.THREAD_DAEMON, true)
        );
        // IO thread processes network events
        worker.getIoThread().execute(() -> handleIOReadyEvents());
        // Worker thread processes business logic
        worker.getWorkerThreadPool().execute(() -> executeBusinessLogic());
    }
}

Advantages:

IO threads focus on network : not blocked by business logic.

Worker thread pool elasticity : dynamically adjusts to workload.

Event‑driven efficiency : reduces thread switches.

5.2 Tomcat Thread Model Comparison

Tomcat's traditional thread model suffers from high thread‑context‑switch overhead, blocking waits, and linear memory growth with thread count.

6. Configuration Flexibility

6.1 Fine‑grained Configuration Capability

@Configuration
public class UndertowConfig {
    @Bean
    public UndertowServletWebServerFactory undertowServletWebServerFactory() {
        UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
        factory.addBuilderCustomizers(builder -> {
            // Enable HTTP/2
            builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true);
            // Buffer options
            builder.setSocketOption(Options.RECEIVE_BUFFER, 1024 * 16);
            builder.setSocketOption(Options.SEND_BUFFER, 1024 * 64);
            // Thread pool
            builder.setIoThreads(Runtime.getRuntime().availableProcessors());
            builder.setWorkerThreads(200);
            // Connection limits
            builder.setServerOption(UndertowOptions.MAX_CONNECTIONS, 10000);
        });
        return factory;
    }
}

6.2 Handler Chain Mechanism

public class CustomHandler implements HttpHandler {
    private final HttpHandler next;
    public CustomHandler(HttpHandler next) { this.next = next; }
    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        long startTime = System.currentTimeMillis();
        try {
            preHandle(exchange); // authentication, logging, etc.
            next.handleRequest(exchange);
        } finally {
            postHandle(exchange, startTime); // statistics, cleanup
        }
    }
    private void preHandle(HttpServerExchange exchange) {
        if (!checkAuthentication(exchange)) {
            exchange.setStatusCode(401);
            exchange.endExchange();
            return;
        }
        logRequest(exchange);
    }
}

7. Practical Case

7.1 Container Migration Practice (E‑commerce Platform)

Before migration (Tomcat): QPS 8000, avg response 25 ms, memory 2 GB, CPU 85 %.

After migration (Undertow): QPS 15000 (+87 %), avg response 12 ms (‑52 %), memory 1.2 GB (‑40 %), CPU 65 % (‑23 %).

7.2 Configuration Optimization Example

# Production Undertow optimisation
server:
  undertow:
    io-threads: 8
    worker-threads: 200
    direct-buffers: true
    buffer-size: 16384
    max-connections: 10000
    max-http-post-size: 10485760
    no-request-timeout: 60000
    drain-wait-time: 20000
    port: 8080
    compression:
      enabled: true
      mime-types: text/html,text/xml,text/plain,application/json

8. How to Migrate?

8.1 Maven Configuration Adjustment

<!-- Exclude Tomcat -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<!-- Include Undertow -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

8.2 Migration Considerations

Servlet API compatibility : ensure code uses standard Servlet API.

WebSocket configuration : Undertow's WebSocket setup differs from Tomcat.

SSL configuration : certificates and SSL settings may need adjustment.

Session management : verify distributed session compatibility.

Conclusion

Large enterprises choose Undertow mainly because:

Performance advantage : higher concurrency, lower memory usage, faster response times.

Efficient resource utilization : fine‑grained control of threads, buffers, and connections reduces operational costs.

Modern architecture : XNIO‑based concurrency model, event‑driven design, and zero‑copy support future protocols like HTTP/2 and QUIC.

Business‑driven needs : massive microservice deployments benefit from the cumulative resource savings.

Recommendation: for new projects with expected high concurrency, prefer Undertow; for existing projects facing performance bottlenecks, consider migrating to Undertow.
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.

JavaperformanceMicroservicesSpringBootundertowWebContainer
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

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.