Understanding Java Stack Traces and Generating or Producing or Capturing Full Java Thread Dump

The article is written for J2SE 6, and Eclipse IDE for Java EE Developers.

Table Of Content

  1. Introduction and Generating Java Stack Traces
  2. System Requirements
  3. Generating or Producing or Capturing Full Java Thread Dump
  4. Manipulating Java Stack Traces
  5. Java Thread Dump Process
  6. Feedback

Introduction and Generating Java Stack Traces

A Java stack trace is a user-friendly snapshot of the threads and monitors in a Java1 Virtual Machine (JVM). Full Java Thread Dump is a complete list of active threads. A java thread dump is a way of finding out what each thread in the JVM is doing at a particular point of time.

JVM generates a stack trace if JVM encounters an internal fault then it will call its own signal handler to print out the threads and monitors information. To generate a Java stack trace either you need to send a signal to JVM and JVM generates a stack trace or use debugging tools or use Java API calls to produce stack traces. On UNIX/LINUX platforms you can send a signal to a program by using the kill command.

  • Find out process PID (process id)
  • ps aux | grep processname
    or
    pidof processname
    

  • Kill process using PID (process id)
  • kill –QUIT process_id
    of
    kill -3 process_id
    

In Windows, you can use the key sequence + \ or + to send a “break” signal to the process and obtain a thread dump.

Getting Started – System Requirements

Generating or Producing or Capturing Full Java Thread Dump

The following example code shows a way to capture stack trace and to programmatically capture a thread dump of the JVM.

package com.bhaveshthaker.threaddumps;

import java.util.Map;

/**
 * Class demonstrates for how to generate or capture stack traces and thread dumps.
 * 
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
public class ThreadDumpDemo
{

    /**
     * Default Constructor.
     */
    public ThreadDumpDemo()
    {
    }

    /**
     * Main method.
     * 
     * @param args none.
     */
    public static void main(String[] args)
    {
        System.out
                .println("==================== Generating Java Stack Traces ====================");
        new ThreadDumpDemo().myMethod001();
        System.out.println("====================== Generating Thread Dump ======================");
        System.out.println(ThreadDumpDemo.generateAndCaptureThreadDump());
    }

    private void myMethod001()
    {
        try
        {
            this.myMethod002();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private void myMethod002() throws Exception
    {
        try
        {
            this.myMethod003();
        }
        catch (Exception e)
        {
            throw new Exception("Exception thrown from myMethod002", e);
        }
    }

    private void myMethod003() throws Exception
    {
        try
        {
            this.myMethod004();
        }
        catch (Exception e)
        {
            throw new Exception("Exception thrown from myMethod003", e);
        }
    }

    private void myMethod004() throws Exception
    {
        throw new Exception("Exception thrown from myMethod004");
    }

    /**
     * Generates or captures thread dump.
     * 
     * @return Thread dump as string.
     */
    public static String generateAndCaptureThreadDump()
    {
        StringBuffer stringBuffer = new StringBuffer();
        Map<Thread, StackTraceElement[]> allStackTracesMap = Thread.getAllStackTraces();

        for (Map.Entry<Thread, StackTraceElement[]> mapEntry : allStackTracesMap.entrySet())
        {
            Thread thread = mapEntry.getKey();
            stringBuffer.append(thread + "\n");

            StackTraceElement[] stackTraceElementArray = mapEntry.getValue();
            for (StackTraceElement stackTraceElement : stackTraceElementArray)
            {
                stringBuffer.append("\t" + stackTraceElement + "\n");
            }
        }
        return stringBuffer.toString();
    }

}

The output of above class will be as follows:

==================== Generating Java Stack Traces ====================
java.lang.Exception: Exception thrown from myMethod002
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.myMethod002(ThreadDumpDemo.java:58)
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.myMethod001(ThreadDumpDemo.java:42)
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.main(ThreadDumpDemo.java:32)
Caused by: java.lang.Exception: Exception thrown from myMethod003
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.myMethod003(ThreadDumpDemo.java:70)
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.myMethod002(ThreadDumpDemo.java:54)
	... 2 more
Caused by: java.lang.Exception: Exception thrown from myMethod004
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.myMethod004(ThreadDumpDemo.java:76)
	at com.bhaveshthaker.threaddumps.ThreadDumpDemo.myMethod003(ThreadDumpDemo.java:66)
	... 3 more
====================== Generating Thread Dump ======================
Thread[Reference Handler,10,system]
	java.lang.Object.wait(Native Method)
	java.lang.Object.wait(Object.java:485)
	java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)
Thread[Finalizer,8,system]
	java.lang.Object.wait(Native Method)
	java.lang.ref.ReferenceQueue.remove(Unknown Source)
	java.lang.ref.ReferenceQueue.remove(Unknown Source)
	java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)
Thread[main,5,main]
	java.lang.Thread.dumpThreads(Native Method)
	java.lang.Thread.getAllStackTraces(Unknown Source)
	com.bhaveshthaker.threaddumps.ThreadDumpDemo.generateAndCaptureThreadDump(ThreadDumpDemo.java:87)
	com.bhaveshthaker.threaddumps.ThreadDumpDemo.main(ThreadDumpDemo.java:35)
Thread[Attach Listener,5,system]
Thread[Signal Dispatcher,9,system]

Manipulating Java Stack Traces

Recently in one of my project, architects decided to not to show Java Stack Traces and instead decided to show one line message explaining issue. This requirement created issues for production support, on how to find where [at which line] the code is failing in production. I came up with following code which resolves the issue.

package com.bhaveshthaker.threaddumps;

/**
 * Utility class for dealing with stack traces.
 * 
 * @author <a href="http://bhaveshthaker.com/">Bhaveshkumar Thaker</a>
 * 
 */
public class StackTraceUtil
{

    /**
     * Default Constructor.
     */
    public StackTraceUtil()
    {
    }

    /**
     * Main method.
     * 
     * @param args none.
     */
    public static void main(String[] args)
    {
        System.out
                .println("==================== Manipulating Java Stack Traces ====================");
        new StackTraceUtil().myMethod001();
    }

    /**
     * Gets all messages from all stack trace causes of Throwable.
     * 
     * @param throwable Object of Throwable.
     * @return String of messages constructed from stack trace.
     */
    public static String getStackTraceCausesMessages(Throwable throwable)
    {
        final StringBuffer stringBuffer = new StringBuffer();
        while (throwable != null)
        {
            stringBuffer.append(throwable.getMessage());
            for (final StackTraceElement stackTraceElement : throwable.getStackTrace())
            {
                if (stackTraceElement.toString().trim()
                        .startsWith("com.bhaveshthaker.threaddumps."))
                {
                    stringBuffer.append(" at " + stackTraceElement);
                    break;
                }
            }
            if (throwable.getCause() != null)
            {
                stringBuffer.append(" || ");
                throwable = throwable.getCause();
            }
            else
            {
                break;
            }
        }
        return stringBuffer.toString();
    }

    private void myMethod001()
    {
        try
        {
            this.myMethod002();
        }
        catch (Exception e)
        {
            System.out.println(StackTraceUtil.getStackTraceCausesMessages(e));
        }
    }

    private void myMethod002() throws Exception
    {
        try
        {
            this.myMethod003();
        }
        catch (Exception e)
        {
            throw new Exception("Exception thrown from myMethod002", e);
        }
    }

    private void myMethod003() throws Exception
    {
        try
        {
            this.myMethod004();
        }
        catch (Exception e)
        {
            throw new Exception("Exception thrown from myMethod003", e);
        }
    }

    private void myMethod004() throws Exception
    {
        throw new Exception("Exception thrown from myMethod004");
    }

}

The output of above class will be as follows:

==================== Manipulating Java Stack Traces ====================
Exception thrown from myMethod002 at com.bhaveshthaker.threaddumps.StackTraceUtil.myMethod002(StackTraceUtil.java:85) || Exception thrown from myMethod003 at com.bhaveshthaker.threaddumps.StackTraceUtil.myMethod003(StackTraceUtil.java:97) || Exception thrown from myMethod004 at com.bhaveshthaker.threaddumps.StackTraceUtil.myMethod004(StackTraceUtil.java:103)

Java Thread Dump Process

The Java processes are paused and all threads simply becomes stop dead. The Main java process gets complete information turn by turn from each thread of what thread was doing in details. The thread dump is sent to standard error, or somewhere else, depending on configurations. The Java process is unpaused and all threads simply continue where they left off. The Java process keeps on running, and the whole process only takes a few seconds. Any activity, even input/output is suspended. After the Java thread dump process has completed, everything returns back to normal stage.

Feedback

I would like to hear from you! If you liked this tutorial, have some suggestions or even some corrections for me, please let me know. I track all user feedback in comments sections.

One Comment

  1. Ramesh V L says:

    Very useful information .. thanks for sharing Bhavesh!

Leave a Reply

Your email address will not be published. Required fields are marked *