Deep dive into three essential areas
1. Measuring Performance Before Changing Anything
Why it matters
Jumping into tuning without data is like fixing a problem you don’t fully understand. You might waste time on something irrelevant or even make performance worse.
How to do it
Start your app with Spring Boot Actuator enabled — it exposes endpoints that show key runtime metrics like request timings, memory use, and thread counts.
Example:
management:
endpoints:
web:
exposure:
include: health, metrics, prometheus
You can then hit /actuator/metrics/http.server.requests to see request latency breakdowns.
Next step
Use these metrics to identify slow endpoints or bottlenecks. If a certain API call is slow, that’s where your tuning effort should begin.
2. Garbage Collection (GC) Tuning with Java 17’s G1 Collector
Why it matters
Garbage Collection impacts your app’s response times and throughput. Long GC pauses cause freezes or delayed responses.
What’s special about G1?
G1 is the default GC in Java 17. It tries to balance throughput and pause times by splitting the heap into regions and collecting them in parallel.
Practical tuning
- Monitor GC logs: start your app with
-Xlog:gc*
to capture GC events. - Use a tool like GCViewer to analyze logs visually.
- Set a pause time target, e.g.,
-XX:MaxGCPauseMillis=200
to hint G1 to try to keep pauses under 200ms. - Adjust heap size based on your app’s memory usage — too small means frequent collections, too large means longer pauses.
What to watch out for
If GC pauses are too long or frequent, check for memory leaks or heavy object churn in your code.
3. Optimizing Database Access: Avoid the N+1 Query Problem
Why it matters
Many apps spend a lot of time waiting for the database. The N+1 problem happens when your app fires one query to get a list, then one query per item to get related data — massively increasing DB load.
How it happens
Imagine fetching a list of orders and then, for each order, fetching the customer details separately. Instead of 1 query + N queries, you want 1 query with a join.
How to fix it in Hibernate
Use fetch joins in JPQL:
select o from Order o join fetch o.customer where o.status = :status
This fetches orders and their customers in one go.
Tools to spot this problem
Enable SQL logging in Hibernate:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
Look for repetitive queries in logs during page loads.
Wrap-Up
By focusing on measurement, GC tuning, and database query optimization, you get tangible performance wins without overwhelming complexity. Master these first — the rest will naturally fall into place.