1) Approaching Memory Leaks
Memory leaks in Java often manifest as OutOfMemoryError exceptions or rising heap usage visible in monitoring dashboards. My approach:
- Reproduce in staging: Apply the same traffic profile (e.g., JMeter load test).
- Collect a heap dump:
jmap -dump:format=b,file=heap.hprof <PID>
- Analyze with tools: Eclipse MAT, VisualVM, or YourKit to detect uncollected references.
- Fix common causes:
- Unclosed streams or ResultSets.
- Static collections holding references.
- Caches without eviction policies (e.g., replace
HashMap
withCaffeine
).
2) Profiling and Fixing High CPU Usage
High CPU can stem from tight loops, inefficient queries, or excessive logging.
- Step 1: Sample threads
jstack <PID> > thread-dump.txt
Identify “hot” threads consuming CPU.
- Step 2: Profile with async profilers like async-profiler or Java Flight Recorder.
java -XX:StartFlightRecording=duration=60s,filename=recording.jfr -jar app.jar
- Step 3: Refactor:
- Replace
String concatenation
in loops withStringBuilder
. - Optimize regex (use
Pattern
reuse instead ofString.matches()
). - Review logging level (DEBUG inside loops is expensive).
- Replace
3) Tuning GC for Low-Latency Services
Garbage collection (GC) can cause pauses. For trading, gaming, or API services, tuning matters:
- Choose the right collector:
G1GC
for balanced throughput and latency (default in recent JDKs).ZGC
orShenandoah
for ultra-low latency workloads (<10ms pauses).
- Sample configs:
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+ParallelRefProcEnabled
- Monitor GC logs with GC Toolkit or Grafana dashboards.
4) Handling Database Bottlenecks
Spring apps often hit bottlenecks in DB queries rather than CPU.
- Enable SQL logging: in
application.properties
spring.jpa.show-sql=true
- Profile queries: Use
p6spy
or database AWR reports. - Fixes:
- Add missing indexes (
EXPLAIN ANALYZE
is your friend). - Batch inserts (
saveAll()
in Spring Data withhibernate.jdbc.batch_size
). - Introduce caching (Spring Cache, Redis) for hot reads.
- Use connection pools like HikariCP with tuned settings:
spring.datasource.hikari.maximum-pool-size=30
- Add missing indexes (