How to get a thread and heap dump of a Java process on Windows that's not running in a console

Viewed 568671

I have a Java application that I run from a console which in turn executes an another Java process. I want to get a thread/heap dump of that child process.

On Unix, I could do a kill -3 <pid> but on Windows AFAIK the only way to get a thread dump is Ctrl-Break in the console. But that only gives me the dump of the parent process, not the child.

Is there another way to get that heap dump?

20 Answers

I recommend the Java VisualVM distributed with the JDK (jvisualvm.exe). It can connect dynamically and access the threads and heap. I have found in invaluable for some problems.

You can send the kill -3 <pid> from Cygwin. You have to use the Cygwin ps options to find windows processes then just send the signal to that process.

You could run jconsole (included with Java 6's SDK) then connect to your Java application. It will show you every Thread running and its stack trace.

You have to redirect output from second java executable to some file. Then, use SendSignal to send "-3" to your second process.

How to get process id of java application?

Execute the command 'jcmd' to get the process id of java applications.

How to get Thread dump?

jcmd PID Thread.print > thread.dump

Reference link

You can even use jstack to get thread dump (jstack PID > thread.dump). Reference link

How to get heap dump?

Use jmap tool to get heap dump. jmap -F -dump:live,format=b,file=heap.bin PID

PID stands for process id of the application. Reference link

If you can't (or don't want) to use the console/terminal for some reason, there is an alternative solution. You can make the Java application print the thread dump for you. The code that collects the Stack Trace is reasonable simple and can be attached to a button or a web interface.

private static String getThreadDump() {
    Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();

    StringBuilder out = new StringBuilder();
    for (Map.Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet()) {
        Thread thread = entry.getKey();
        StackTraceElement[] elements = entry.getValue();
        out.append(String.format("%s | prio=%d | %s", thread.getName(), thread.getPriority(), thread.getState()));
        out.append('\n');

        for (StackTraceElement element : elements) {
            out.append(element.toString()).append('\n');
        }
        out.append('\n');
    }
    return out.toString();
}

This method will return a string that looks like this:

main | prio=5 | RUNNABLE
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1607)
Main.getThreadDump(Main.java:8)
Main.main(Main.java:36)

Monitor Ctrl-Break | prio=5 | RUNNABLE
java.net.PlainSocketImpl.initProto(Native Method)
java.net.PlainSocketImpl.<clinit>(PlainSocketImpl.java:45)
java.net.Socket.setImpl(Socket.java:503)
java.net.Socket.<init>(Socket.java:424)
java.net.Socket.<init>(Socket.java:211)
com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:59)

Finalizer | prio=8 | WAITING
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

Reference Handler | prio=10 | WAITING
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:502)
java.lang.ref.Reference.tryHandlePending(Reference.java:191)
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

For those interested in a Java 8 version with streams, the code is even more compact:

private static String getThreadDump() {
    Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
    StringBuilder out = new StringBuilder();
    allStackTraces.forEach((thread, elements) -> {
        out.append(String.format("%s | prio=%d | %s", thread.getName(), thread.getPriority(), thread.getState()));
        out.append('\n');

        Arrays.stream(elements).forEach(element -> out.append(element.toString()).append('\n'));
        out.append('\n');
    });
    return out.toString();
}

You can easily test this code with:

System.out.print(getThreadDump());

Below java code is used to get the Heap Dump of a Java Process by providing a remote process' PID. The Program uses Remote JMX connection to dump heap to a file. It may be helpful for some one. Doesn't require jmap.

import java.lang.management.ManagementFactory;
import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.reflect.Method;

public class HeapDumper {

public static final String HOST = "192.168.11.177";
public static final String PORT = "1600";
public static final String FILE_NAME = "heapDump.hprof";
public static final String FOLDER_PATH = "C:/";
private static final String HOTSPOT_BEAN_NAME ="com.sun.management:type=HotSpotDiagnostic";

public static void main(String[] args) {
    if(args.length == 0) {
        System.out.println("Enter PID of the Java Process !!!");
        return;
    }
    
    String pidString = args[0];
    int pid = -1;
    if(pidString!=null && pidString.length() > 0) {
        try {
            pid = Integer.parseInt(pidString);
        }
        catch(Exception e) {
            System.out.println("PID is not Valid !!!");
            return;
        }
    }
    boolean isHeapDumpSuccess = false;
    boolean live = true;
    if(pid > 0) {
        MBeanServerConnection beanServerConn = getJMXConnection();
        
        if(beanServerConn!=null) {
            Class clazz = null;
            String dumpFile = FOLDER_PATH+"/"+FILE_NAME;
            try{
                clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
                Object hotspotMBean = ManagementFactory.newPlatformMXBeanProxy(beanServerConn, HOTSPOT_BEAN_NAME, clazz);
                Method method = clazz.getMethod("dumpHeap", new Class[]{String.class , boolean.class});
                method.setAccessible(true);
                method.invoke(hotspotMBean , new Object[] {dumpFile, new Boolean(live)});
                isHeapDumpSuccess = true;
            }
            catch(Exception e){
                e.printStackTrace();
                isHeapDumpSuccess = false;
            }
            finally{
                clazz = null;
            }
        }
    }
    
    if(isHeapDumpSuccess){
        System.out.println("HeapDump is Success !!!");
    }
    else{
        System.out.println("HeapDump is not Success !!!");
    }
}

private static MBeanServerConnection getJMXConnection() {
    MBeanServerConnection mbeanServerConnection = null;
    String urlString = "service:jmx:rmi:///jndi/rmi://" + HOST + ":" + PORT + "/jmxrmi";
    try {
        JMXServiceURL url = new JMXServiceURL(urlString);
        JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
        mbeanServerConnection = jmxConnector.getMBeanServerConnection();
        System.out.println("JMX Connection is Success for the URL :"+urlString);
    }
    catch(Exception e) {
        System.out.println("JMX Connection Failed !!!");
    }
    return mbeanServerConnection;
}

}

Maybe jcmd?

Jcmd utility is used to send diagnostic command requests to the JVM, where these requests are useful for controlling Java Flight Recordings, troubleshoot, and diagnose JVM and Java Applications.

The jcmd tool was introduced with Oracle's Java 7 and is particularly useful in troubleshooting issues with JVM applications by using it to identify Java processes' IDs (akin to jps), acquiring heap dumps (akin to jmap), acquiring thread dumps (akin to jstack), viewing virtual machine characteristics such as system properties and command-line flags (akin to jinfo), and acquiring garbage collection statistics (akin to jstat). The jcmd tool has been called "a swiss-army knife for investigating and resolving issues with your JVM application" and a "hidden gem."

Here’s the process you’ll need to use in invoking the jcmd:

  1. Go to jcmd <pid> GC.heap_dump <file-path>
  2. In which
  3. pid: is a Java Process Id, for which the heap dump will be captured Also, the
  4. file-path: is a file path in which the heap dump is be printed.

Check it out for more information about taking Java heap dump.

Inorder to take thread dump/heap dump from a child java process in windows, you need to identify the child process Id as first step.

By issuing the command: jps you will be able get all java process Ids that are running on your windows machine. From this list you need to select child process Id. Once you have child process Id, there are various options to capture thread dump and heap dumps.

Capturing Thread Dumps:

There are 8 options to capture thread dumps:

  1. jstack
  2. kill -3
  3. jvisualVM
  4. JMC
  5. Windows (Ctrl + Break)
  6. ThreadMXBean
  7. APM Tools
  8. jcmd

Details about each option can be found in this article. Once you have capture thread dumps, you can use tools like fastThread, Samuraito analyze thread dumps.

Capturing Heap Dumps:

There are 7 options to capture heap dumps:

  1. jmap

  2. -XX:+HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. Programmatic Approach

  7. Administrative consoles

Details about each option can be found in this article. Once you have captured heap dump, you may use tools like Eclipse Memory Analysis tool, HeapHero to analyze the captured heap dumps.

Visualvm followup:

If you "can't connect" to your running JVM from jvisualvm because you didn't start it with the right JVM arguments (and it's on remote box), run jstatd on the remote box, then, assuming you have a direct connection, add it as a "remote host" in visualvm, double click the host name, and all other JVM's on that box will magically show up in visualvm.

If you don't have "direct connection" to ports on that box, you can also do this through a proxy.

Once you can see the process you want, drill into it in jvisualvm and use monitor tab -> "heapdump" button.

Related