How to Detect and Fix JVM CPU Spikes, Deadlocks, and Memory Leaks
Learn practical JVM tuning techniques by identifying high CPU usage, diagnosing deadlocks, and uncovering memory leaks using tools such as top, jstack, jps, jstat, and jmap, with step‑by‑step commands and code examples to help you optimize Java application performance.
Assume you already understand the runtime data areas and common garbage collection algorithms, as well as the GC collectors supported by Hotspot.
High CPU Usage
High CPU usage should be analyzed case by case. If a sudden traffic surge due to a business activity causes the CPU to spike and then drop after the activity ends, this is normal. However, if the server has limited resources (e.g., a single‑core CPU), even a modest traffic increase can exhaust CPU, and upgrading the hardware is advisable.
If the CPU remains high for a long period, it often indicates code with excessive loops or a dead loop. The investigation steps are:
(1) Use the top command to view CPU usage.
The process ID shown by top matches the VMID displayed by the jps tool.
(2) Use top -Hp <pid> to view thread‑level CPU usage.
Thread ID 7287 is continuously consuming CPU.
(3) Convert the thread ID to hexadecimal.
# printf "%x" 7287
1c77Remember the hexadecimal value for the next step.
(4) Use jstack to inspect the thread stack.
# jstack 7268 | grep 1c77 -A 10
"http-nio-8080-exec-2" #16 daemon prio=5 os_prio=0 tid=0x00007fb66ce81000 nid=0x1c77 runnable [0x00007fb639ab9000]
java.lang.Thread.State: RUNNABLE
at com.spareyaya.jvm.service.EndlessLoopService.service(EndlessLoopService.java:19)
...The stack trace shows the thread executing EndlessLoopService.service at line 19, confirming the infinite loop.
Deadlock
Deadlocks are less obvious because the involved threads are in a waiting state (WAITING or TIMED_WAITING) and do not consume CPU, but they block request processing, leading to timeouts.
Use jstack to detect deadlocks:
(1) Find the Java process with jps .
# jps -l
8737 sun.tools.jps.Jps
8682 jvm-0.0.1-SNAPSHOT.jar(2) Run jstack and look for the deadlock report at the end of the output.
Java stack information for the threads listed above:
===================================================
"Thread-4":
at com.spareyaya.jvm.service.DeadLockService.service2(DeadLockService.java:35)
- waiting to lock <0x00000000f5035ae0> (a java.lang.Object)
- locked <0x00000000f5035af0> (a java.lang.Object)
...
"Thread-3":
at com.spareyaya.jvm.service.DeadLockService.service1(DeadLockService.java:27)
- waiting to lock <0x00000000f5035af0> (a java.lang.Object)
- locked <0x00000000f5035ae0> (a java.lang.Object)
...
Found 1 deadlock.The output clearly shows the two threads waiting on each other's locks.
Memory Leak
Although Java has automatic garbage collection, memory leaks can still occur when objects remain reachable.
A leak manifests as gradually decreasing available memory and eventually an OutOfMemoryError. Simply increasing -Xmx does not solve the root cause.
Example program that triggers a leak:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
Main main = new Main();
while (true) {
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
main.run();
}
}
private void run() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(() -> { /* do something */ });
}
}
}Run with limited heap:
-Xms20m -Xmx20m -XX:+PrintGCGC logs show continuous collections without reclaiming enough memory, eventually leading to:
java.lang.OutOfMemoryError: Java heap spaceTo locate the leaking objects, enable heap dumps on OOM:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.binAnalyze the dump with Eclipse MAT; it often reveals many Thread and ThreadPoolExecutor instances that were never shut down.
Properly shutting down thread pools (or using a singleton) prevents the leak.
Additional Diagnostic Tools
(1) Locate the process ID with jps .
C:\...\>jps -l
24836 org.example.net.Main
...(2) Use jstat to monitor GC activity.
# jstat -gcutil -t -h8 24836 1000
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
29.1 32.81 0.00 23.48 85.92 92.84 84.13 14 0.339 0 0.000 0.339
...Columns show survivor spaces, Eden, old generation, Metaspace, GC counts and times.
(3) Dump a live heap with jmap without waiting for OOM.
# jmap -dump:live,format=b,file=heap.bin 24836Conclusion
These three scenarios—high CPU, deadlock, and memory leak—illustrate how to use JVM tools to pinpoint performance problems. Effective JVM tuning requires careful measurement and iterative adjustment rather than blind parameter changes.
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.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
