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

眉间皱痕 提交于 2019-11-26 05:52:49

问题


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?


回答1:


You can use jmap to get a dump of any process running, assuming you know the pid.

Use Task Manager or Resource Monitor to get the pid. Then

jmap -dump:format=b,file=cheap.hprof <pid>

to get the heap for that process.




回答2:


You are confusing two different java dumps. kill -3 generates a thread dump, not a heap dump.

Thread dump = stack traces for each thread in the JVM output to stdout as text.

Heap dump = memory contents for the JVM process output to a binary file.

To take a thread dump on Windows, CTRL+BREAK if your JVM is the foreground process is the simplest way. If you have a unix-like shell on Windows like Cygwin or MobaXterm, you can use kill -3 {pid} like you can in Unix.

To take a thread dump in Unix, CTRL+C if your JVM is the foreground process or kill -3 {pid} will work as long as you get the right PID for the JVM.

With either platform, Java comes with several utilities that can help. For thread dumps, jstack {pid} is your best bet. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Just to finish the dump question out: Heap dumps are not commonly used because they are difficult to interpret. But, they have a lot of useful information in them if you know where/how to look at them. The most common usage is to locate memory leaks. It is a good practice to set the -D on the java command-line so that the heap dump is generated automatically upon an OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError But, you can manually trigger a heap dump, also. The most common way is to use the java utility jmap.

NOTE: this utility is not available on all platforms. As of JDK 1.6, jmap is available on Windows.

An example command-line would look something like

jmap -dump:file=myheap.bin {pid of the JVM}

The output "myheap.bin" is not human readable (for most of us), and you will need a tool to analyze it. My preference is MAT. http://www.eclipse.org/mat/




回答3:


I think the best way to create .hprof file in Linux process is with jmap command. For example: jmap -dump:format=b,file=filename.hprof {PID}




回答4:


In addition to using the mentioned jconsole/visualvm, you can use jstack -l <vm-id> on another command line window, and capture that output.

The <vm-id> can be found using the task manager (it is the process id on windows and unix), or using jps.

Both jstack and jps are include in the Sun JDK version 6 and higher.




回答5:


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.




回答6:


Try one of below options.

  1. For 32 bit JVM:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
    
  2. For 64 bit JVM (explicitly quoting):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
    
  3. For 64 bit JVM with G1GC algorithm in VM parameters (Only live objects heap is generated with G1GC algorithm):

    jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
    

Related SE question: Java heap dump error with jmap command : Premature EOF

Have a look at various options of jmap at this article




回答7:


If you are on server-jre 8 and above you can use this:

jcmd PID GC.heap_dump /tmp/dump



回答8:


If you want a heapdump on out-of-memory, you can start Java with the option -XX:-HeapDumpOnOutOfMemoryError

c.f. JVM Options reference page




回答9:


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.




回答10:


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.




回答11:


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




回答12:


If you are using JDK 1.6 or above, You can use jmap command to take a heap Dump of a Java process, condition is you should known ProcessID.

If you are on Windows Machine, you can use Task Manager to get PID. For Linux machine you can use varieties of command like ps -A | grep java or netstat -tupln | grep java or top | grep java, depends on your application.

Then you can use the command like jmap -dump:format=b,file=sample_heap_dump.hprof 1234 where 1234 is PID.

There are varieties of tool available to interpret the hprof file. I will recommend Oracle's visualvm tool, which is simple to use.




回答13:


I wrote a small batch script for Java 8 (using PsExec and jcmd) named jvmdump.bat, which dumps the threads, heap, system properties, and JVM args.

:: set the paths for your environment
set PsExec=C:\Apps\SysInternals\PsExec.exe
set JAVA_HOME=C:\Apps\Java\jdk1.8.0_121
set DUMP_DIR=C:\temp

@echo off

set PID=%1

if "%PID%"=="" (
    echo usage: jvmdump.bat {pid}
    exit /b
)

for /f "tokens=2,3,4 delims=/ " %%f in ('date /t') do set timestamp_d=%%h%%g%%f
for /f "tokens=1,2 delims=: " %%f in ('time /t') do set timestamp_t=%%f%%g
set timestamp=%timestamp_d%%timestamp_t%
echo datetime is: %timestamp%

echo ### Version >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.version >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Uptime >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.uptime >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Command >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.command_line >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Flags >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.flags >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

echo. >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
echo ### Properties >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"
%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% VM.system_properties >>"%DUMP_DIR%\%PID%-%timestamp%-jvm.log"

%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% Thread.print -l >"%DUMP_DIR%\%PID%-%timestamp%-threads.log"

%PsExec% -s %JAVA_HOME%\bin\jcmd.exe %PID% GC.heap_dump "%DUMP_DIR%\%PID%-%timestamp%-heap.hprof"

echo Dumped to %DUMP_DIR%

It must be run in the same Windows session of the user that started the JVM, so if you connect through Remote Desktop you might need to launch a command prompt in Session 0 and run it from there. e.g.

%PsExec% -s -h -d -i 0 cmd.exe

This will prompt you (click the taskbar icon at the bottom) to View the message in the interactive session, which will take you to the new console in the other session from which you can run the jvmdump.bat script.




回答14:


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());



回答15:


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.




回答16:


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

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;
}

}




回答17:


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.




回答18:


On a Oracle JDK, we have a command called jmap (available in the bin folder of Java Home). usage of the command comes as follows

jmap (option) (pid)

Example: jmap -dump:live,format=b,file=heap.bin (pid)



来源:https://stackoverflow.com/questions/407612/how-to-get-a-thread-and-heap-dump-of-a-java-process-on-windows-thats-not-runnin

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!