1. Look at the server first
top
vmstat 2 5
free -m
| What you see | What it means |
|---|---|
High us, java at 100 percent or more per core | The JVM is busy computing: a loop, GC or real load. Go to step 2 to 4 |
High wa (I/O wait) | Waiting for the disk: swapping, logging to a slow disk or the database |
High st (steal) | The host gives your VPS less CPU than it needs. Not your application; talk to the provider or move |
si/so above zero in vmstat, swap in use | The server is swapping. Reduce memory use. Java and swap are a bad mix |
| Low CPU but the app is slow | Waiting: database, remote API, DNS, locks. Go to step 5 |
2. Is it garbage collection?
sudo -u appuser jstat -gcutil $(pgrep -f myapp.jar) 1000 10
Columns FGC and FGCT are the number and total time of full collections. If they grow every few seconds while O (old generation) stays near 100, the heap is too small or the application leaks memory. Raise the heap if the server has room, or check for a leak with a heap dump. Choose the collector for your size: choose a garbage collector.
3. Find the hot thread
top -H -p $(pgrep -f myapp.jar) # note the PID of the thread with highest %CPU
printf "%x\n" THREAD_PID # convert it to hexadecimal
sudo -u appuser jcmd $(pgrep -f myapp.jar) Thread.print | grep -A 15 "nid=0xHEX"
Replace THREAD_PID and HEX with your values. The stack trace shows where the thread spends time. Take two or three snapshots a few seconds apart. If the same method is on top each time, that is your hot spot. Names starting with GC Thread or G1 mean garbage collection, which sends you back to step 2.
4. Startup spike or JIT
High CPU for the first minute after a start is normal. The JIT compiler works hard while the application warms up. On a one-core server this makes the site slow right after every restart. Plan restarts for quiet times, or add a core.
5. Everything else is waiting
- Database. Turn on the slow query log (MySQL
slow_query_log, PostgreSQLlog_min_duration_statement). A missing index is the single most common reason for a slow Java application. - Thread pool exhausted. A thread dump shows many threads blocked in the same place, often waiting for a database connection. Look for the pool settings (HikariCP
maximumPoolSize). - External calls and DNS. Add timeouts to every HTTP client. A slow partner API makes your own threads pile up.
- Disk.
iostat -x 2shows if the disk is saturated, for example by verbose logging.
6. Keep the evidence
Next time it happens you want numbers. Turn on monitoring and GC logging (-Xlog:gc*:file=/var/log/myapp/gc.log). Graphs of CPU, memory and response time answer most of these questions without guessing.
Questions about java app slow or high cpu
What does CPU steal mean?
The hypervisor gave CPU time to other virtual machines when yours wanted it. Consistent steal above a few percent means the host is oversold.
Last updated October 29, 2026.