How to Diagnose and Optimize High CPU Usage in Java Applications – Full Guide
When a Java application’s CPU spikes, this step‑by‑step guide walks you through a visual flowchart, essential Linux commands, thread‑dump analysis, common stack patterns, GC checks, flame‑graph profiling, and JVM tuning tips to quickly pinpoint and resolve the root cause.
Overall Investigation Flowchart
Begin with a visual flowchart that outlines the complete troubleshooting workflow for high‑CPU Java processes.
Detailed Investigation Steps
1. Identify High‑CPU Java Process and Threads
List processes sorted by CPU usage: top then press Shift+P to sort descending.
Show threads of the target Java process: top -Hp <pid>. Note the thread ID (TID) shown in the nid column.
Convert the numeric TID to hexadecimal so it matches the nid value in a thread dump: printf "%x\n" <tid>.
2. Export Thread Dump and Locate the High‑CPU Thread
jstack <pid> > thread_dump.logA typical thread‑dump line looks like:
"Thread-1" #25 prio=5 os_prio=0 tid=0x00007f3c58001000 nid=0x35bc runnable
at com.example.demo.Service.loop(Service.java:45)tid – JVM internal thread identifier.
nid – Operating‑system thread ID in hexadecimal; matches the nid from top -Hp.
Status runnable (or running ) usually indicates the thread that is consuming CPU.
Common Stack Patterns and Problem Types
Infinite loop / busy‑wait – Thread stays in RUNNABLE with repeated method calls. Root cause: a while(true) loop without blocking. Solution: add Thread.sleep(), Object.wait(), or other blocking primitives.
Heavy computation – CPU‑intensive algorithm executed repeatedly. Root cause: complex algorithm or processing large data sets. Solution: introduce parallelism, cache results, or optimise the algorithm.
Severe lock contention – Thread appears in BLOCKED waiting on a monitor. Root cause: overly coarse synchronized lock. Solution: shrink lock granularity or replace with java.util.concurrent.locks.Lock (e.g., ReentrantLock, ReadWriteLock).
Frequent GC – Stack shows a GC worker thread (e.g., "GC task thread"). Root cause: memory pressure or many short‑lived objects. Solution: increase heap size, tune GC parameters, or investigate memory leaks.
Excessive logging – Thread repeatedly executes logger.debug() inside a loop. Solution: throttle logging, increase log level, or add conditional logging.
Thread‑pool rejection – Executor threads stay busy and tasks are rejected. Root cause: queue size too small or insufficient core pool size. Solution: enlarge the work queue or increase corePoolSize / maximumPoolSize.
When Threads Relate to GC – How to Check
1. Use jstat to Monitor GC Frequency
jstat -gc <pid> 5000Key fields:
YGC – Number of young generation collections.
FGC – Number of full GC events.
FGCT – Cumulative time spent in full GC.
S0C/S1C/Eden – Capacities of the young generation spaces.
Interpretation:
If FGC keeps increasing together with a high FGCT , full GC is occurring frequently.
If heap usage does not drop after GC, a possible memory leak exists.
2. Dump Heap for Further Analysis
jmap -dump:format=b,file=/tmp/heap.hprof <pid>Typical analysis tools:
Eclipse MAT (Memory Analyzer) – look for Leak Suspects .
VisualVM or JProfiler – inspect object retention graphs and GC behaviour.
Flame Graph Analysis – Visualizing CPU Hotspots
./profiler.sh -d 30 -e cpu -f /tmp/cpu.svg <pid>Advantages of flame graphs:
Instantly identify methods that dominate CPU consumption.
Support both Java frames and native frames.
Longer stacked frames correspond to higher CPU usage.
JVM Parameter Tuning Template
-Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/opt/heap.hprof \
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/log/gc.logReal‑World Cases
Infinite loop – Stack stuck in service.doWork(). Fix by adding sleep or await.
Lock contention – Multiple threads blocked on a synchronized method. Fix by using ReadWriteLock or splitting the lock.
Redis timeout retries – Retry logic without back‑off. Fix by adding exponential back‑off and rate limiting.
Frequent Full GC – Rising FGC count. Fix by increasing heap size or locating large objects that cause promotion.
Summary
Find the Java process: top (or ps -ef | grep java).
Identify the high‑CPU thread: top -Hp <pid> and convert nid to hex.
Export the stack trace: jstack <pid> and locate the runnable thread.
Classify the root cause – code issue (infinite loop, heavy computation, lock contention, logging) vs. GC issue.
Use auxiliary tools for deeper insight: jstat, jmap, Eclipse MAT, VisualVM, flame‑graph scripts, or arthas.
Apply appropriate optimisations: refactor code, adjust JVM flags, increase heap, or add monitoring alerts.
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.
Ray's Galactic Tech
Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow together!
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.
