Master performance debugging. JProfiler, heap dumps, GC logs, flame graphs. Real-world diagnosis: what to look for and how to find root causes fast.
JOptimize Team
Scenario: Your Java app's P99 latency jumped from 45ms to 2.1 seconds. Suddenly. No code changes. No deployment. Just... slow.
The question: How do you find the problem in 10 minutes instead of 10 hours?
This guide teaches you exactly that.
# Check JVM metrics first jps -l # List running JVMs jcmd <pid> VM.system_properties | grep memory jcmd <pid> GC.heap_dump_live /tmp/heap.hprof # Dump heap
What you're looking for:
# GC logs java -Xlog:gc*:file=gc.log:time,level,tags -Xmx4g MyApp # CPU profiling jcmd <pid> JFR.start duration=60s filename=/tmp/profile.jfr # Lock contention jcmd <pid> Thread.print > /tmp/threads.txt
JOptimize detects this automatically
Scan your Java project in 30 seconds — free, no credit card.
Clue 1: Database CPU high, app CPU normal
App CPU: 20% DB CPU: 95% ← Problem is here Memory: Normal GC pauses: Normal
Clue 2: Connection pool maxed out
Connection pool: 10 connections Active connections: 10 (all busy) Queued requests: 45 waiting
Clue 3: Slow query log
SELECT * FROM orders o JOIN order_items oi ON o.id = oi.order_id WHERE o.user_id = ? -- Execution time: 500ms (should be 10ms)
A marketing feature added 2 weeks ago:
@PostMapping("/dashboard") public Dashboard getDashboard() { User user = userService.getUser(); // 1 query List<Order> orders = orderService.getOrders(user.id); // N queries // ... }
With 50k users/day accessing dashboard:
@Query("SELECT DISTINCT o FROM Order o " + "LEFT JOIN FETCH o.items " + "WHERE o.userId = ?1") List<Order> getOrdersWithItems(Long userId);
Result:
What to look for:
2026-10-01T10:00:00.000+0000: [GC (Allocation Failure) [PSYoungGen: 1024M→512M(1024M)] 2048M→1536M(4096M), 0.5234 secs] 2026-10-01T10:00:30.000+0000: [Full GC [PSYoungGen: 1024M→0M(1024M)] [ParOldGen: 3072M→2500M(3072M)] 4096M→2500M(4096M), 3.4234 secs]
Red flags:
Width = time spent Height = call stack depth Color = random (helps distinguish sections)
"http-nio-8080-exec-1" #21 daemon prio=5 os_prio=0 java.lang.Thread.State: BLOCKED (on object monitor) at com.example.UserService.getUser(UserService.java:42) - waiting to lock <0x00007f8b8c0a0200> (a java.util.HashMap) at com.example.DashboardController.getDashboard
What this means:
java -XX:+UnlockDiagnosticVMOptions \ -XX:+TraceClassLoading \ -XX:+PrintGCDetails \ -XX:+PrintGCTimeStamps \ MyApp
✅ Minute 1-2: Identify bottleneck
✅ Minute 3-5: Gather evidence
✅ Minute 6-10: Analyze & act
JOptimize catches issues before they need debugging:
Result: Problems never make it to production, so you never need to debug them.
Fast debugging comes from:
Master these, and performance mysteries become solvable in minutes.
Get started: joptimize.io
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.