Thread dump generator

0

A thread dump is a snapshot of the details of all threads that are part of the process. The thread details consists of thread name, thread ID, thread state (NEW, RUNNABLE, WAITING, BLOCKED etc) and stack trace which shows the contents of a thread’s stack.
Thread dump help us to diagnose problems like CPU spikes, deadlocks, helps us to better optimize application and JVM performance.
In this article we will see example of full thread dump and dump showing deadlocks.
We first need to retrieve the managed bean for the thread system.

ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

Let’s look into some of the important methods of ThreadMXBean which we will be using in building the thread dump.

  • isObjectMonitorUsageSupported An object monitor is the lock acquired when entering a synchronization block or method on that object. This method returns true if if the Java virtual machine supports monitoring of object monitor usage
  • isSynchronizerUsageSupported An ownable synchronizer is a synchronizer that may be exclusively owned by a thread and uses AbstractOwnableSynchronizer or its subclass to implement its synchronization property.
  • dumpAllThreads The thread info for all live threads with stack trace and synchronization information. We will see its exact contents later.
  • getAllThreadIds Live thread IDs
  • findDeadlockedThreads Finds cycles of threads that are in deadlock waiting to acquire object monitors or ownable synchronizers
  • findMonitorDeadlockedThreads Finds cycles of threads that are in deadlock waiting to acquire object monitors.
  • getThreadInfo(threadIDs, stacktrace depth) The thread info for each of the threads represented by the thread IDs, with stack trace

In the below class we build thread dump for all the threads and the case where we just want to create dump for threads stuck in a deadlock.

ThreadDumpGenerator:

package com.javarticles.diagnostics;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

/**
 * Generates thread dumps.
 */
public final class ThreadDumpGenerator {
 
    private ThreadDumpGenerator() {
    }

    public static String dumpAllThreads() {
        System.out.println("Generating full thread dump...");
        StringBuilder s = new StringBuilder();
        s.append("Full thread dump ");
        return dump(getAllThreads(), s);
    }

    public static String dumpDeadlocks() {
        System.out.println("Generating dead-locked threads dump...");
        StringBuilder s = new StringBuilder();
        s.append("Deadlocked thread dump ");
        return dump(findDeadlockedThreads(), s);
    }

    private static String dump(ThreadInfo[] infos, StringBuilder s) {
        header(s);
        appendThreadInfos(infos, s);
        System.out.println("\n" + s);
        return s.toString();
    }

    public static ThreadInfo[] findDeadlockedThreads() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        if (threadMXBean.isSynchronizerUsageSupported()) {
            long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
            if (deadlockedThreads == null || deadlockedThreads.length == 0) {
                return null;
            }
            return threadMXBean.getThreadInfo(deadlockedThreads, true, true);
        } else {
            long[] monitorDeadlockedThreads = threadMXBean.findMonitorDeadlockedThreads();
            return getThreadInfos(threadMXBean, monitorDeadlockedThreads);
        }
    }

    public static ThreadInfo[] getAllThreads() {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        if (threadMXBean.isObjectMonitorUsageSupported()
                && threadMXBean.isSynchronizerUsageSupported()) {
            return threadMXBean.dumpAllThreads(true, true);
        }
        long[] allThreadIds = threadMXBean.getAllThreadIds();
        return getThreadInfos(threadMXBean, allThreadIds);
    }

    private static ThreadInfo[] getThreadInfos(ThreadMXBean threadMXBean, long[] threadIds) {
        if (threadIds == null || threadIds.length == 0) {
            return null;
        }
        return threadMXBean.getThreadInfo(threadIds, Integer.MAX_VALUE);
    }

    private static void header(StringBuilder s) {
        s.append(System.getProperty("java.vm.name"));
        s.append(" (");
        s.append(System.getProperty("java.vm.version"));
        s.append(" ");
        s.append(System.getProperty("java.vm.info"));
        s.append("):");
        s.append("\n\n");
    }

    private static void appendThreadInfos(ThreadInfo[] infos, StringBuilder s) {
        if (infos == null || infos.length == 0) {
            return;
        }
        for (ThreadInfo info : infos) {
            s.append(info);
        }
    }
}

Next we need to create some threads involved in some activity. In our example below, we have used three category of threads.

  1. TransferTask – Transfers data from one queue to another
  2. UserChat1 and UserChat2 – User chat threads to reproduce a deadlock state
  3. OwnableSynchronizerThread – ReentrantLock and ReentrantReadWriteLock are examples of ownable synchronizers. We are using a ReentrantReadWriteLock here so that we can see the locked ownable synchronizer in the thread dump.

ThreadDumpGeneratorExample:

package com.javarticles.diagnostics;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ThreadDumpGeneratorExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> source = new LinkedBlockingQueue<>();
        SourceTask sourceTask = new SourceTask(source);
        Thread sourceThread = new Thread(sourceTask, "SourceThread");
        sourceThread.start();

        try {
            Thread.sleep(100);
        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        TransferTask transferTask = new TransferTask(source);
        Thread taskThread = new Thread(transferTask, "TaskThread");
        taskThread.start();

        final UserChat sam = new UserChat("Sam");
        final UserChat joe = new UserChat("Joe");
        new Thread(new Runnable() {
            public void run() {
                sam.connect(joe);
            }
        }, "UserChat1").start();
        new Thread(new Runnable() {
            public void run() {
                joe.connect(sam);
            }
        }, "UserChat2").start();
        
        WriteAccessThread writeAccessThread = new WriteAccessThread();
        new Thread(writeAccessThread, "OwnableSynchronizerThread").start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Generating thread dump");
        ThreadDumpGenerator.dumpAllThreads();

        System.out.println("Generating deallock dump");
        ThreadDumpGenerator.dumpDeadlocks();
        System.exit(1);
    }

    private static class TransferTask implements Runnable {
        BlockingQueue<Integer> transferTo = new LinkedBlockingQueue<>();
        BlockingQueue<Integer> transferFrom;

        TransferTask(BlockingQueue<Integer> transferFrom) {
            this.transferFrom = transferFrom;
        }

        @Override
        public void run() {
            while (true) {
                transferTo.offer(transferFrom.poll());
            }
        }
    }

    private static class SourceTask implements Runnable {
        BlockingQueue<Integer> sourceQueue;

        SourceTask(BlockingQueue<Integer> sourceQueue) {
            this.sourceQueue = sourceQueue;
        }

        @Override
        public void run() {
            int i = 0;
            while (true) {
                this.sourceQueue.add(i++);
            }
        }

    }

    private static class WriteAccessThread implements Runnable {
        private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        @Override
        public void run() {
            lock.writeLock().lock();
            doSomething();
            lock.writeLock().unlock();
        }
        private void doSomething() {
            List<Integer> l = new ArrayList<>();
            for (int i = 0; i < 100000000; i++) {
                l.add(i);
            }
        }
        
    }
     
    private static class UserChat {
        private final String name;

        public UserChat(String name) {
            this.name = name;
        }

        public String getName() {
            return this.name;
        }

        public synchronized void connect(UserChat userChat) {
            System.out.format("I am %s\n", this.name);
            userChat.hello(this);
        }

        public synchronized void hello(UserChat userChat) {
            System.out.format("Hello %s!%n This is %s", this.name, userChat.getName());
        }
    }

}

Some of the threads belong to the Java application you are running, while others are JVM internal threads. The highlighted output shows details about the ownable synchronizer and the locks.
We wouldn’t have got this additional info had we not requested for locked monitors and locked ownable synchronizers.

threadMXBean.dumpAllThreads(true, true)

Output:

I am Sam
I am Joe
Generating thread dump
Generating full thread dump...

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode):

"OwnableSynchronizerThread" Id=15 RUNNABLE
	at java.util.ArrayList.grow(ArrayList.java:258)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
	at java.util.ArrayList.add(ArrayList.java:462)
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$WriteAccessThread.doSomething(ThreadDumpGeneratorExample.java:101)
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$WriteAccessThread.run(ThreadDumpGeneratorExample.java:95)
	at java.lang.Thread.run(Thread.java:748)

	Number of locked synchronizers = 1
	- [email protected]25154f

"UserChat2" Id=14 BLOCKED on [email protected]5c647e05 owned by "UserChat1" Id=13
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.hello(ThreadDumpGeneratorExample.java:124)
	-  blocked on [email protected]5c647e05
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.connect(ThreadDumpGeneratorExample.java:120)
	-  locked [email protected]70dea4e
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$2.run(ThreadDumpGeneratorExample.java:36)
	at java.lang.Thread.run(Thread.java:748)

"UserChat1" Id=13 BLOCKED on [email protected]70dea4e owned by "UserChat2" Id=14
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.hello(ThreadDumpGeneratorExample.java:124)
	-  blocked on [email protected]70dea4e
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.connect(ThreadDumpGeneratorExample.java:120)
	-  locked [email protected]5c647e05
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$1.run(ThreadDumpGeneratorExample.java:31)
	at java.lang.Thread.run(Thread.java:748)

"TaskThread" Id=12 RUNNABLE
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$TransferTask.run(ThreadDumpGeneratorExample.java:67)
	at java.lang.Thread.run(Thread.java:748)

"SourceThread" Id=11 RUNNABLE
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$SourceTask.run(ThreadDumpGeneratorExample.java:83)
	at java.lang.Thread.run(Thread.java:748)

"Attach Listener" Id=5 RUNNABLE

"Signal Dispatcher" Id=4 RUNNABLE

"Finalizer" Id=3 WAITING on [email protected]
	at java.lang.Object.wait(Native Method)
	-  waiting on [email protected]
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" Id=2 WAITING on [email protected]
	at java.lang.Object.wait(Native Method)
	-  waiting on [email protected]
	at java.lang.Object.wait(Object.java:502)
	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"main" Id=1 RUNNABLE
	at sun.management.ThreadImpl.dumpThreads0(Native Method)
	at sun.management.ThreadImpl.dumpAllThreads(ThreadImpl.java:454)
	at com.javarticles.diagnostics.ThreadDumpGenerator.getAllThreads(ThreadDumpGenerator.java:54)
	at com.javarticles.diagnostics.ThreadDumpGenerator.dumpAllThreads(ThreadDumpGenerator.java:19)
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample.main(ThreadDumpGeneratorExample.java:50)


Generating deallock dump
Generating dead-locked threads dump...

Deadlocked thread dump Java HotSpot(TM) 64-Bit Server VM (25.151-b12 mixed mode):

"UserChat2" Id=14 BLOCKED on [email protected]5c647e05 owned by "UserChat1" Id=13
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.hello(ThreadDumpGeneratorExample.java:124)
	-  blocked on [email protected]5c647e05
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.connect(ThreadDumpGeneratorExample.java:120)
	-  locked [email protected]70dea4e
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$2.run(ThreadDumpGeneratorExample.java:36)
	at java.lang.Thread.run(Thread.java:748)

"UserChat1" Id=13 BLOCKED on [email protected]70dea4e owned by "UserChat2" Id=14
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.hello(ThreadDumpGeneratorExample.java:124)
	-  blocked on [email protected]70dea4e
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$UserChat.connect(ThreadDumpGeneratorExample.java:120)
	-  locked [email protected]5c647e05
	at com.javarticles.diagnostics.ThreadDumpGeneratorExample$1.run(ThreadDumpGeneratorExample.java:31)
	at java.lang.Thread.run(Thread.java:748)

Download the source code

This was an example about generating thread dump. We have shown it for all the live threads and for the threads in deadlock.

You can download the source code here: threadDumpGenerator.zip
Share.

Comments are closed.