Spring Boot Performance Optimization and Scalability Guide
Why a Spring Boot Application Starts Slowly
Section titled “Why a Spring Boot Application Starts Slowly”Common Causes
Section titled “Common Causes”-
Large number of auto-configurations
- Spring Boot scans many auto-configuration classes using
spring.factories/AutoConfiguration.imports.
- Spring Boot scans many auto-configuration classes using
-
Component scanning
@ComponentScanscans packages for Spring-managed components.
-
Classpath scanning
- Libraries such as Hibernate, Jackson, and Bean Validation scan classes.
-
Bean initialization
- Thousands of beans may be created during startup.
-
Database initialization
- Hibernate schema validation or DDL generation increases startup time.
-
Heavy logging configuration
-
Large dependency tree
Optimization Techniques
Section titled “Optimization Techniques”Disable unnecessary auto-configurations
Section titled “Disable unnecessary auto-configurations”@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})Or exclude it via properties instead of code:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationEnable lazy initialization
Section titled “Enable lazy initialization”spring.main.lazy-initialization=trueReduce component scanning
Section titled “Reduce component scanning”@ComponentScan(basePackages = "com.myapp.service")Use Spring Boot 3 AOT / Native Image
Section titled “Use Spring Boot 3 AOT / Native Image”Ahead-of-time compilation significantly reduces startup time.
Disable DevTools in production
Section titled “Disable DevTools in production”DevTools provides automatic restart, live reload, and improved logging during development — useful locally, but it adds overhead and should be excluded from production builds (it’s automatically disabled when running from a packaged JAR, but avoid relying on that alone if using an unusual deployment setup).
Optimize Hibernate startup
Section titled “Optimize Hibernate startup”spring.jpa.hibernate.ddl-auto=nonespring.jpa.open-in-view=falseUse JVM Class Data Sharing (CDS)
Section titled “Use JVM Class Data Sharing (CDS)”Measure Startup
Section titled “Measure Startup”spring.main.application-startup=bufferingRecords timing for each startup step so slow phases can be identified directly, rather than guessed at.
Startup Flow
Section titled “Startup Flow”flowchart TD
A[Application Starts] --> B[Classpath Scan]
B --> C[Auto Configuration]
C --> D[Component Scan]
D --> E[Bean Creation]
E --> F[Hibernate Initialization]
F --> G[Application Ready]
Interview Answer
Section titled “Interview Answer”Startup delay mainly comes from classpath scanning, bean initialization, Hibernate initialization, and auto-configuration. Optimize using lazy initialization, limiting component scanning, disabling unused auto-configurations, and using Spring Boot AOT/native compilation.
Reducing Memory Usage
Section titled “Reducing Memory Usage”Techniques
Section titled “Techniques”- Tune JVM heap
-Xms512m-Xmx1024m- Use lazy beans
@Bean@Lazy- Disable Open Session in View
spring.jpa.open-in-view=false- Stream large datasets
Stream<User> findAllBy();- Optimize caching
@Cacheable("users")Use bounded caches such as Caffeine or Redis with eviction.
- Reduce unnecessary object creation
- Tune database connection pools
Interview Answer
Section titled “Interview Answer”Reduce bean initialization, limit cache sizes, stream large datasets, optimize connection pools, and tune JVM heap settings.
Optimizing Database Performance
Section titled “Optimizing Database Performance”Best Practices
Section titled “Best Practices”Create indexes
Section titled “Create indexes”CREATE INDEX idx_user_email ON users(email);Use pagination
Section titled “Use pagination”Page<User> findAll(Pageable pageable);Solve the N+1 query problem
Section titled “Solve the N+1 query problem”The problem: one query loads parent records, then one additional query loads child records for each parent.
1 query -> EmployeesN queries -> Orders for each employeeUsing @EntityGraph
@EntityGraphUsing JOIN FETCH
@Query("SELECT o FROM Order o JOIN FETCH o.items")Using batch fetching (loads related entities in batches instead of one-by-one — distinct from the INSERT/UPDATE batching below):
hibernate.default_batch_fetch_size=10Enable batching
Section titled “Enable batching”spring.jpa.properties.hibernate.jdbc.batch_size=50Use projections
Section titled “Use projections”interface UserView { String getName();}Use HikariCP connection pooling
Section titled “Use HikariCP connection pooling”Enable second-level cache
Section titled “Enable second-level cache”Example:
Hibernate + RedisLazyInitializationException
Section titled “LazyInitializationException”Occurs when a lazy-loaded association is accessed after the Hibernate session has closed.
User user = userRepository.findById(id);user.getOrders().size(); // LazyInitializationExceptionSolutions
Section titled “Solutions”JOIN FETCH
Section titled “JOIN FETCH”@Query("SELECT u FROM User u JOIN FETCH u.orders")DTO projection
Section titled “DTO projection”SELECT new UserDTO(u.name,o.name)Transactional service
Section titled “Transactional service”@Transactionalpublic User getUser() {}EntityGraph
Section titled “EntityGraph”@EntityGraph(attributePaths = "orders")spring.jpa.open-in-view=trueConnection Pooling
Section titled “Connection Pooling”Spring Boot uses HikariCP by default.
Configuration
Section titled “Configuration”spring.datasource.hikari.maximum-pool-size=20spring.datasource.hikari.minimum-idle=5spring.datasource.hikari.connection-timeout=30000Why connection pooling?
Section titled “Why connection pooling?”Without pooling, every request opens and closes a database connection.
With pooling, existing connections are reused.
flowchart TD
A[Application] --> B[Connection Pool]
B --> C[(Database)]
High Throughput APIs
Section titled “High Throughput APIs”Techniques
Section titled “Techniques”@Async- Spring WebFlux (non-blocking I/O)
- Redis/Caffeine caching
- ThreadPoolTaskExecutor
- Rate limiting (Resilience4j, Bucket4j)
- CDN for static content
- Kafka/RabbitMQ for asynchronous processing
- HTTP/2 (multiplexed requests over a single connection) where the client and server both support it
@AsyncScaling Spring Boot for Millions of Users
Section titled “Scaling Spring Boot for Millions of Users”Strategies
Section titled “Strategies”- Horizontal scaling
- Stateless services
- Redis session store
- Redis cluster caching
- Read replicas
- Database sharding
- API Gateway
- Kafka/RabbitMQ
flowchart TD
LB[Load Balancer]
LB --> I1[Instance 1]
LB --> I2[Instance 2]
LB --> I3[Instance 3]
I1 --> DB[(Database)]
I2 --> DB
I3 --> DB
In practice, an API Gateway typically sits in front of the load balancer:
flowchart TD
C[Client] --> G[API Gateway]
G --> LB[Load Balancer]
LB --> M[Microservices]
Monitoring Thread Pool Usage
Section titled “Monitoring Thread Pool Usage”Add the Actuator dependency (spring-boot-starter-actuator) and check /actuator/metrics for:
executor.activeexecutor.completedexecutor.queue.sizeFor deeper Micrometer/Prometheus/Grafana setup, see the Spring Boot production monitoring guide.
Additional Tools
Section titled “Additional Tools”jstack- VisualVM
- JConsole
- YourKit
Interview Answer
Section titled “Interview Answer”Monitor thread pools using Spring Boot Actuator metrics, Micrometer with Prometheus and Grafana dashboards, JVM tools such as VisualVM, and thread dump analysis using jstack.
Key Takeaways
Section titled “Key Takeaways”- Startup performance is primarily affected by scanning, auto-configuration, bean creation, and Hibernate initialization.
- Memory usage is improved through JVM tuning, lazy initialization, streaming, and cache optimization.
- Database performance depends on indexing, pagination, batching, projections, and efficient fetching.
- HikariCP is the default connection pool and should be tuned for production workloads.
- High-throughput systems rely on asynchronous processing, caching, thread-pool tuning, and horizontal scaling.
- Thread pool health is visible through Actuator executor metrics, complemented by JVM diagnostic tools like
jstackand VisualVM.