JVM Troubleshooting

How do you read a Java thread dump?

A thread dump is a snapshot of every thread in a JVM. Learn how to identify deadlocks, blocked threads, lock contention, and stuck requests.

JVMTroubleshootingConcurrencySenior Java

What Is A Thread Dump?

A thread dump is a snapshot of every thread currently running inside a JVM.

Think of it as taking a photograph of the application at a particular moment in time.

When production systems appear frozen, slow, deadlocked, or unresponsive, a thread dump is often one of the first things engineers capture.

When Would You Take One?

  • Application appears hung
  • Requests never finish
  • High CPU usage
  • Deadlock suspected
  • Lock contention suspected
  • Performance investigation

How Do You Generate A Thread Dump?

The JVM generates thread dumps.

The most common tools are:

text
jstack <pid>

or:

text
kill -3 <pid>

Many developers know:

text
kill -9

which forcefully terminates a process.

But:

text
kill -3

is special for Java applications.

Instead of terminating the JVM, it tells the JVM to dump information about all running threads into the logs.

The Four States You Will See Most Often

RUNNABLE

Thread is actively running or ready to run.

BLOCKED

Waiting to acquire a lock.

WAITING

Waiting indefinitely.

TIMED_WAITING

Waiting with a timeout.

The Simplest Deadlock Example

Imagine a money transfer system with two accounts.

java
Thread A:
lock(account1)
lock(account2)

Thread B:
lock(account2)
lock(account1)

If both threads execute simultaneously:

Thread A

Locks account1
Waiting for account2

Thread B

Locks account2
Waiting for account1
Neither thread can continue. This is a deadlock.

A Deadlock Program You Can Run Yourself

java
public class DeadlockDemo {

    private static final Object account1 = new Object();
    private static final Object account2 = new Object();

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            synchronized (account1) {

                sleep();

                synchronized (account2) {
                    System.out.println("Transfer completed");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (account2) {

                sleep();

                synchronized (account1) {
                    System.out.println("Transfer completed");
                }
            }
        });

        t1.start();
        t2.start();
    }

    private static void sleep() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Capturing The Thread Dump

Start the application.

Find the process id:

text
jps

Then:

text
jstack <pid>

What The Thread Dump Might Show

text
Thread-0:
waiting to lock account2

locked account1


Thread-1:
waiting to lock account1

locked account2

The JVM may even helpfully print:

text
Found one Java-level deadlock

How To Spot Lock Contention

Deadlocks are rare.

Lock contention is much more common.

text
BLOCKED
BLOCKED
BLOCKED
BLOCKED
BLOCKED

If many threads are blocked waiting for the same lock, you may have a throughput bottleneck.

How To Spot A Hung Request

text
http-nio-8080-exec-17

at PaymentService.callBank()

at RestTemplate.exchange()

at ...

The stack trace tells you exactly where the request is currently stuck.

Interview Answer

A thread dump is a snapshot of all JVM threads. I use it to diagnose deadlocks, blocked threads, lock contention, and hung requests. I typically generate it using jstack or kill -3 and then look for thread states, lock ownership, and repeating stack traces.

Final Takeaway

Reading thread dumps looks intimidating at first, but in practice most investigations start with a few simple questions: What are threads waiting for? Are they blocked? Are they deadlocked? What code are they stuck in?