Java
Thread Dump
Analysis
Debugging
Performance Tuning

How to analyze a java thread dump?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A Java thread dump is a snapshot of every thread inside a JVM at a specific moment. It is one of the fastest ways to diagnose deadlocks, stuck requests, CPU spikes, and thread pool exhaustion because it shows what each thread is doing and what it is waiting for.

The most useful analysis comes from reading several dumps taken a few seconds apart. A single dump can tell you what is blocked right now, but repeated dumps reveal whether threads are making progress or stuck in the same stack frames.

Capture the Dump Correctly

You can generate a dump with jcmd, jstack, or a signal such as kill -3 on Unix-like systems.

bash
1jcmd 12345 Thread.print > thread-dump-1.txt
2sleep 5
3jcmd 12345 Thread.print > thread-dump-2.txt
4sleep 5
5jcmd 12345 Thread.print > thread-dump-3.txt

Three dumps spaced a few seconds apart are often more valuable than one. If the same busy thread stays in the same method in all three files, that is a strong clue.

Read the Thread Header First

Each thread starts with a header that tells you the thread name, priority, native identifier, and state. The state is the first quick signal:

  • 'RUNNABLE means the thread is eligible to run, often consuming CPU or doing I/O'
  • 'BLOCKED means it is waiting for a monitor lock'
  • 'WAITING means it is parked until another thread signals it'
  • 'TIMED_WAITING means it is sleeping or waiting with a timeout'

Example excerpt:

text
1"http-nio-8080-exec-12" #56 daemon prio=5 os_prio=0 tid=0x00007f... nid=0x4a03 waiting for monitor entry
2   java.lang.Thread.State: BLOCKED (on object monitor)
3    at com.example.OrderService.process(OrderService.java:87)
4    - waiting to lock <0x0000000701234567> (a java.lang.Object)
5    at com.example.OrderController.submit(OrderController.java:42)

Start by asking a simple question: is this a normal waiting thread, or a thread that should not be stuck?

Look for Repeated Problem Patterns

Blocked Threads

If many request threads are BLOCKED, find the monitor they are trying to enter and the thread that owns it. A single synchronized section around slow work can serialize your entire service.

Waiting Thread Pools

A pool full of waiting worker threads can be normal. A pool with too few workers and many pending requests is not. Match the dump with application metrics so you know whether inactivity is healthy or pathological.

Busy Loops

A thread in RUNNABLE is not automatically bad, but if the same stack trace appears in multiple dumps without moving, it may be spinning:

java
1while (!shutdownRequested) {
2    if (queue.isEmpty()) {
3        continue;
4    }
5    process(queue.remove());
6}

This kind of loop burns CPU because it polls instead of blocking. Replacing it with a blocking queue is usually better:

java
1BlockingQueue<Job> queue = new LinkedBlockingQueue<>();
2
3while (!shutdownRequested) {
4    Job job = queue.take();
5    process(job);
6}

Identify Deadlocks and Lock Contention

The JVM can sometimes report a deadlock explicitly near the end of the dump. Even when it does not, you can still infer one by tracing which thread owns which monitor and which monitor each thread is waiting for.

If thread A waits for a lock held by thread B, and thread B waits for a lock held by thread A, you have a classic cycle. The fix is normally architectural: consistent lock ordering, smaller critical sections, or replacing nested locking with higher-level concurrency primitives.

Connect Stack Frames to Real Code

Do not stop at thread states. The important part is the top application frame. Framework code tells you where execution currently sits, but your package names usually reveal the real bottleneck.

For example, if many request threads stop at:

text
at com.example.UserCache.refresh(UserCache.java:112)

go read that method. Look for synchronized blocks, slow database calls, unbounded retries, or network requests made while holding a lock.

Common Pitfalls

  • Analyzing only one dump and drawing strong conclusions from it. Repeated dumps are much more reliable.
  • Treating every WAITING thread as a problem. Idle executor workers are often supposed to wait.
  • Focusing on framework frames and ignoring the first relevant application frame.
  • Missing lock ownership details. The key clue is often the monitor line, not just the state name.
  • Capturing a dump long after the incident. For transient production issues, timing matters.

Summary

  • Capture several thread dumps a few seconds apart.
  • Use thread states to triage, then read the top application frames closely.
  • 'BLOCKED threads usually point to lock contention or deadlocks.'
  • Repeated RUNNABLE stacks can indicate CPU-heavy loops or stuck native calls.
  • The best fixes usually come from correlating thread dumps with application code and runtime metrics.

Course illustration
Course illustration

All Rights Reserved.