Avoid Hidden Spring Boot Pitfalls: Optimize Tomcat, HikariCP, JPA, and More
Spring Boot’s “convention over configuration” hides many default settings that can cause performance bottlenecks and runtime failures, from Tomcat and HikariCP connection limits to JPA lazy loading, timezone serialization, logging, caching, file upload limits, thread pools, and transaction timeouts, and the article shows how to tune each.
Spring Boot promotes "convention over configuration", but many defaults are unsuitable for production and can lead to hidden performance problems and runtime errors.
Tomcat Connection Pool
Spring Boot uses Tomcat as the default web container, yet its default connection pool settings become a bottleneck under high concurrency. The default maximum connections and threads are both 200, causing request queuing when traffic exceeds this limit.
server:
tomcat:
max-connections: 10000 # maximum connections
threads:
max: 800 # maximum worker threads
min-spare: 100 # minimum idle threads
accept-count: 100 # queue length
connection-timeout: 20000Additionally, the default connection timeout is unlimited, which can keep connections open indefinitely.
Database Connection Pool
Spring Boot defaults to HikariCP for the database pool, but the default maximum pool size of 10 is far too low for most real‑world applications.
spring:
datasource:
hikari:
maximum-pool-size: 50
minimum-idle: 10
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
leak-detection-threshold: 60000Pay special attention to leak-detection-threshold; it is disabled by default, so connection leaks may go unnoticed.
JPA Lazy Loading
When Spring Boot integrates JPA, lazy loading is enabled by default. While useful, it often triggers N+1 query problems.
@Entity
public class User {
@Id
private Long id;
@OneToMany(fetch = FetchType.LAZY) // default is LAZY
private List<Order> orders;
}Fetching the orders collection for each user generates a separate SQL query, leading to 101 queries for 100 users. Solutions include using @EntityGraph or a JOIN FETCH query.
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders")
List<User> findAllWithOrders();Jackson Time‑Zone Serialization
Jackson, used by Spring Boot for JSON handling, defaults to the system time‑zone. In distributed deployments this causes inconsistent timestamps.
spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
serialization:
write-dates-as-timestamps: falseIf servers run in different zones, the same instant may be serialized differently, which is problematic for internationalized applications.
Log Configuration
Spring Boot ships with Logback, but the default setup lacks log file rotation and cleanup, causing log files to grow indefinitely. The default log level is INFO, which can generate excessive logs in production.
logging:
file:
name: app.log
logback:
rollingpolicy:
max-file-size: 100MB
max-history: 30
total-size-cap: 3GBCache Configuration
The @Cacheable annotation uses a simple ConcurrentHashMap by default, which has no eviction policy or size limit. In high‑traffic scenarios this can lead to unbounded memory growth.
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=10000,expireAfterWrite=600sMonitoring Endpoints
Spring Boot Actuator exposes many endpoints (health, info, metrics, etc.). While useful in development, exposing them in production can leak sensitive information. Only expose necessary endpoints and secure them appropriately.
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: when-authorizedFile Upload Size Limits
The default limits (1 MB per file, 10 MB total request) are often too low for real applications, leading to MaxUploadSizeExceededException errors.
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
file-size-threshold: 2KB
location: /tmpThe file-size-threshold determines when files are written to disk versus kept in memory; setting it appropriately balances memory usage and I/O performance.
Async Thread‑Pool Configuration
Using @Async defaults to SimpleAsyncTaskExecutor, which creates a new thread for each task, exhausting resources under load. Configure a proper thread pool with sensible core and max sizes.
spring:
task:
execution:
pool:
core-size: 8
max-size: 16
queue-capacity: 100
keep-alive: 60s
thread-name-prefix: async-task-
scheduling:
pool:
size: 4
thread-name-prefix: scheduling-For CPU‑bound work, thread count should match CPU cores; for I/O‑bound work, a higher multiplier is appropriate.
Static Resource Caching Strategy
Spring Boot does not set HTTP cache headers for static assets by default, causing browsers to re‑download unchanged files on every request.
spring:
web:
resources:
cache:
cachecontrol:
max-age: 365d
cache-public: true
chain:
strategy:
content:
enabled: true
paths: /**
cache: true
static-locations: classpath:/static/Enabling content‑based versioning adds a hash to filenames (e.g., style-abc123.css), allowing browsers to cache assets effectively.
Database Transaction Timeout
The @Transactional annotation has no default timeout, so long‑running transactions can hold locks and block other operations.
@Transactional(timeout = 30, rollbackFor = Exception.class)
public void batchProcess(List<Data> dataList) {
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
List<Data> batch = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
processBatch(batch);
}
}Splitting large jobs into smaller transactions releases locks sooner and improves concurrency; always specify rollbackFor to ensure proper rollback on any exception.
Conclusion
Spring Boot’s “convention over configuration” saves effort but also hides assumptions that may not fit every production scenario. Proactively reviewing and tuning defaults—connection pools, lazy loading, serialization, logging, caching, file limits, thread pools, and transaction settings—prevents many avoidable incidents and leads to more reliable, performant applications.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
