Tomcat thread pools: does the connector's internal pool release idle threads like a shared Executor?
0 reputation · 10 Apr 2026, 18:53 UTC
0 reputation · 10 Apr 2026, 18:53 UTC
Goal: shrink the memory footprint of a low-traffic Tomcat deployment enough to fit a cheaper hosting tier, by tuning the HTTP Connector's documented thread-pool limits rather than the heap alone.
Two documented mechanisms are in play. The connector exposes maxThreads (long-standing default 200) and minSpareThreads (default 10), and worker threads reserve JVM stack whether or not requests arrive. Separately, a shared <Executor> (StandardThreadExecutor) documents maxIdleTime, default 60000 ms, terminating idle threads above minSpareThreads. The catch: whether the connector-internal pool reaps idle threads after a burst looks implementation- and version-sensitive, while the shared Executor documents that reaping explicitly, so the two are not interchangeable in behavior.
Constraints: peak concurrency must still fit inside maxThreads, and a very low minSpareThreads can add thread-creation latency after a quiet period. Assumption: Tomcat 9 or 10.x with the NIO HTTP/1.1 connector; the config docs shipped with the installed distribution are authoritative for defaults.
32775 reputation · 11 Apr 2026, 01:52 UTC
The Tomcat 9/10 NIO connector's internal thread pool does not shrink down to zero after idle periods. It maintains at least minSpareThreads threads alive indefinitely. Only threads beyond that threshold are eligible for termination after the executor's keepAliveTime (default 60 seconds when using a shared Executor).
ThreadPoolExecutor where corePoolSize = minSpareThreads and allowCoreThreadTimeOut = false by default. Core threads never expire.maxIdleTime for idle-thread termination. When configured, it applies to all threads above minSpareThreads.minSpareThreads × thread_stack_size. With default minSpareThreads=10 and typical 1MB stack, that's ~10MB reserved even during total idle.minSpareThreads too low (e.g., 1-2) risks latency spikes after idle as new core threads are spawned for incoming requests.minSpareThreads and optionally configure a shared Executor with a shorter maxIdleTime for more aggressive reaping of excess threads.# After a burst, check thread count drops to minSpareThreads only jstack <pid> | grep 'http-nio' | wc -l # Confirm connector uses internal pool (no shared Executor configured) cat $CATALINA_HOME/conf/server.xml | grep -A5 'Connector.*HTTP'
For low-traffic deployments, set:
minSpareThreads=4 (minimum practical floor to avoid latency)maxThreads=50 (sufficient for bursts)Executor with maxIdleTime=30000 for faster cleanupWhat is your peak concurrent request count during traffic bursts? This determines the safe upper bound for maxThreads and whether the default 200 is overkill for your workload.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.