Why a Spring Boot API Took 100ms Extra: Tracing Tomcat’s Hidden Jar Loading Bug

A Spring Boot channel service showed an unexpected 100 ms latency; by systematically checking network, using curl, and employing Arthas to trace Spring MVC and Tomcat internals, the author discovered a Tomcat‑embed bug that repeatedly loads Swagger‑UI JAR resources, which is resolved by upgrading Tomcat.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Why a Spring Boot API Took 100ms Extra: Tracing Tomcat’s Hidden Jar Loading Bug

Background

The company runs a channel system that merely forwards requests, performs message conversion and parameter validation, and sits between upstream services and third‑party channels. After code optimisation the response time still fell short of the target, with about 100 ms extra time observed between the service’s internal timing and the client‑side latency.

Investigation Process

Code analysis

The system is a typical Spring Boot web application using the embedded Tomcat server. No custom filters or interceptors were found, so business code was initially ruled out.

Call‑flow analysis

The request flow is Nginx → Channel System. Ping tests from the host to the Nginx server and from Nginx to the channel server showed sub‑millisecond latency, confirming the network is not the cause.

By bypassing Nginx and calling the service directly via curl http://127.0.0.1:7744/send, the first request took about 73 ms while the second request (made immediately after) took only 3 ms, suggesting a caching effect.

Further testing revealed that if a pause is introduced between requests, the latency jumps back to >70 ms, indicating that some resource is being re‑loaded after its cache expires.

Using Arthas for Deep Tracing

Arthas, Alibaba’s open‑source Java diagnostic tool, was employed. The trace command was first run on Spring MVC entry points, showing that Spring MVC itself consumed only ~18 ms of the total 115 ms observed by the client.

Since the remaining time could not be accounted for, the investigation shifted to Tomcat. Using stack org.springframework.web.servlet.DispatcherServlet identified the call chain leading to org.apache.coyote.http11.Http11Processor.service. Tracing this class highlighted a 129 ms hotspot.

Further tracing of org.apache.catalina.webresources.TomcatJarInputStream revealed repeated calls to createZipEntry:

+---[min=0.004452ms,max=34.479307ms,total=74.206249ms,count=31] org.apache.catalina.webresources.TomcatJarInputStream:getNextJarEntry() #117

The method loads JAR entries for META-INF/ and META-INF/MANIFEST.MF . The logs showed 31 such calls consuming a total of ~74 ms. Using watch on TomcatJarInputStream.createZipEntry exposed the actual resource name being loaded: swagger-ui , a Swagger UI library packaged as a JAR. Removing the Swagger JAR from the project eliminated the mysterious 70 ms delay.

Root Cause Analysis

The issue stems from a bug in Tomcat‑embed version 8.5.31 (used by Spring Boot 2.0.2.RELEASE). The method org.apache.catalina.mapper.Mapper#internalMapWrapper re‑validates static resources on every request, causing the JAR’s static files to be re‑read. Tomcat caches these resources in org.apache.catalina.webresources.Cache with a default TTL of 5000 ms, so consecutive requests hit the cache and are fast, while a pause forces a cache miss and the costly JAR loading. The bug does not appear in local IDE runs because the Spring Boot packaging plugin creates a different class loading path, avoiding the problematic code path.

Solution

Upgrade the embedded Tomcat version to 8.5.40 or later, which contains the fix. This can be done by:

Setting <tomcat.version>8.5.40</tomcat.version> in the Maven <properties> section of the Spring Boot parent POM.

Or upgrading Spring Boot itself to 2.1.0.RELEASE or newer, which bundles a newer Tomcat version.

After the upgrade, the extra 70 ms latency disappears and the service meets the required response time.

Key Takeaways

Network latency may be negligible; hidden framework overhead can dominate response time.

Arthas trace and watch commands are powerful for pinpointing slow paths in production.

Embedded server bugs can surface only in packaged deployments, not in local IDE runs.

Upgrading the embedded container often resolves obscure performance regressions.

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.

Spring BoottomcatArthasSwaggerPerformance debuggingEmbedded Tomcat
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.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.