it-swarm.com.de

So erhalten Sie einen Thread- und Heap-Dump eines Java-Prozesses unter Windows, der nicht in einer Konsole ausgeführt wird

Ich habe eine Java-Anwendung, die ich von einer Konsole aus ausführen kann, die wiederum einen anderen Java-Prozess ausführt. Ich möchte einen Thread/Heap-Dump dieses untergeordneten Prozesses erhalten.

Unter Unix könnte ich einen kill -3 <pid> machen, aber unter Windows AFAIK ist der einzige Weg, einen Thread-Dump zu erhalten, die Strg-Pause in der Konsole. Aber das gibt mir nur den Dump des übergeordneten Prozesses, nicht das Kind.

Gibt es eine andere Möglichkeit, diesen Heap-Dump zu erhalten?

213
macgreg

Sie können jmap verwenden, um einen Speicherauszug eines Prozesses abzurufen, vorausgesetzt, Sie kennen pid.

Verwenden Sie den Task-Manager oder den Ressourcenmonitor, um pid abzurufen. Dann

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

um den Haufen für diesen Prozess zu bekommen.

349
rkaganda

Sie verwechseln zwei verschiedene Java-Dumps. kill -3 generiert einen Thread-Dump, keinen Heap-Dump.

Thread dump = Stapelspuren für jeden Thread in der JVM-Ausgabe als Text ausgeben.

Heap Dump = Speicherinhalt für die Ausgabe des JVM-Prozesses in eine Binärdatei.

So erstellen Sie einen Thread-Dump unter Windows: CTRL+BREAK Wenn Ihre JVM im Vordergrund steht, ist dies der einfachste Weg. Wenn Sie eine Unix-ähnliche Shell unter Windows wie Cygwin oder MobaXterm haben, können Sie kill -3 {pid} wie in Unix verwenden.

So erstellen Sie einen Thread-Dump unter Unix: CTRL+C Wenn Ihre JVM der Vordergrundprozess ist oder kill -3 {pid} funktioniert, solange Sie die richtige PID für die JVM erhalten.

Java wird auf beiden Plattformen mit verschiedenen Hilfsprogrammen ausgeliefert, die Abhilfe schaffen können. Für Thread-Dumps ist jstack {pid} die beste Wahl. http://docs.Oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Um die Dump-Frage abzuschließen: Heap-Dumps werden nicht häufig verwendet, da sie schwer zu interpretieren sind. Aber sie enthalten viele nützliche Informationen, wenn Sie wissen, wo und wie Sie sie ansehen müssen. Die häufigste Verwendung ist das Auffinden von Speicherlecks. Es wird empfohlen, den -D in der Java-Befehlszeile so festzulegen, dass der Heap-Dump bei einem OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError, automatisch generiert wird. Sie können jedoch auch manuell einen Heap-Dump auslösen. Am häufigsten wird das Java-Dienstprogramm jmap verwendet.

HINWEIS: Dieses Dienstprogramm ist nicht auf allen Plattformen verfügbar. Ab JDK 1.6 ist jmap unter Windows verfügbar.

Eine Beispielbefehlszeile würde ungefähr so ​​aussehen

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

Die Ausgabe "myheap.bin" ist für die meisten von uns nicht lesbar und Sie benötigen ein Tool, um sie zu analysieren. Meine Präferenz ist MAT. http://www.Eclipse.org/mat/

110
Derek

Ich denke, der beste Weg, eine .hprof-Datei in einem Linux-Prozess zu erstellen, ist mit dem Befehl jmap. Zum Beispiel: jmap -dump:format=b,file=filename.hprof {PID}

29
Roberto Flores

Neben der Verwendung von jconsole/visualvm können Sie auch jstack -l <vm-id> in einem anderen Befehlszeilenfenster verwenden und diese Ausgabe erfassen.

Die <vm-id> kann mit dem Task-Manager (es ist die Prozess-ID unter Windows und Unix) oder mit jps gefunden werden.

Sowohl jstack als auch jps sind in Sun JDK Version 6 und höher enthalten.

17
ankon

Ich empfehle die Java VisualVM, die mit dem JDK (jvisualvm.exe) verteilt wird. Es kann eine dynamische Verbindung herstellen und auf die Threads und den Heap zugreifen. Ich habe für einige Probleme von unschätzbarem Wert gefunden.

16
Lawrence Dol

Versuchen Sie eine der folgenden Optionen.

  1. Für 32-Bit-JVM:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
    
  2. Für 64-Bit-JVM (explizit zitiert):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
    
  3. Für 64-Bit-JVM mit G1GC-Algorithmus in den Parametern VM (Nur der Heap von Live-Objekten wird mit dem G1GC-Algorithmus generiert): 

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

Zugehörige SE-Frage: Java-Heap-Dump-Fehler mit jmap-Befehl: Vorzeitig EOF

Verschiedene Optionen von jmap finden Sie in diesem article

14
Ravindra babu

Wenn Sie einen Heapspeicherauszug auf Speicherplatz benötigen, können Sie Java mit der Option -XX:-HeapDumpOnOutOfMemoryError starten. 

c.f. Referenzseite für JVM-Optionen

12

Wenn Sie sich auf Server-jre 8 oder höher befinden, können Sie Folgendes verwenden:

jcmd PID GC.heap_dump /tmp/dump
12
Atul Soman

Sie können jconsole (im Lieferumfang von Java 6 SDK enthalten) ausführen und dann eine Verbindung zu Ihrer Java-Anwendung herstellen. Es zeigt Ihnen jeden laufenden Thread und dessen Stack-Trace.

6
Steve Kuo

Sie können den kill -3 <pid> von Cygwin aus senden. Sie müssen die Cygwin-Optionen ps verwenden, um Windows-Prozesse zu finden und das Signal einfach an diesen Prozess zu senden.

5
krosenvold

Sie müssen die Ausgabe von der zweiten ausführbaren Java-Datei in eine andere Datei umleiten. Verwenden Sie dann SendSignal , um send "-3" zu Ihrem zweiten Prozess.

4
Yoni Roit

Wenn Sie JDK 1.6 oder höher verwenden, können Sie mit dem Befehl jmap einen Heap-Dump eines Java-Prozesses erstellen. Voraussetzung ist, dass Sie ProcessID kennen sollten.

Wenn Sie sich auf einem Windows-Computer befinden, können Sie den Task-Manager zum Abrufen der PID verwenden. Für Linux-Maschinen können Sie verschiedene Befehlsarten wie ps -A | grep Java oder netstat -tupln | grep Java oder top | grep Java verwenden, abhängig von Ihrer Anwendung.

Dann können Sie den Befehl wie jmap -dump:format=b,file=sample_heap_dump.hprof 1234 verwenden, wobei 1234 PID ist.

Es gibt verschiedene Varianten von tool , um die hprof-Datei zu interpretieren. Ich werde das visualvm-Tool von Oracle empfehlen, das einfach zu verwenden ist. 

3
Badal

Ich habe ein kleines Batch-Skript für Java 8 (mit PsExec und jcmd) mit dem Namen jvmdump.bat geschrieben, das Threads, Heap, Systemeigenschaften und JVM-Argumente erstellt.

:: 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%

Sie muss in derselben Windows-Sitzung des Benutzers ausgeführt werden, der die JVM gestartet hat. Wenn Sie eine Verbindung über Remote Desktop herstellen, müssen Sie möglicherweise eine Eingabeaufforderung in Session 0 starten und von dort aus ausführen. z.B.

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

Dadurch werden Sie in der interaktiven Sitzung aufgefordert, auf View the message zu klicken (klicken Sie auf das Taskleistensymbol unten), um Sie zur neuen Konsole in der anderen Sitzung zu führen, von der aus Sie das jvmdump.bat-Skript ausführen können.

2
isapir

Visualvm-Nachbereitung:

Wenn Sie von jvisualvm aus keine Verbindung zu Ihrer laufenden JVM herstellen können, weil Sie sie nicht mit den richtigen JVM-Argumenten gestartet haben (und sich auf einer Remote-Box befindet), führen Sie jstatd auf der Remote-Box aus. Fügen Sie ihn als "Remote-Host" in visualvm hinzu, doppelklicken Sie auf den Hostnamen, und alle anderen JVMs in diesem Feld werden auf magische Weise in visualvm angezeigt.

Wenn Sie keine "direkte Verbindung" zu den Ports auf dieser Box haben, können Sie dies auch über proxy tun. 

Wenn Sie den gewünschten Prozess sehen können, bohren Sie in jvisualvm hinein und verwenden Sie die Registerkarte "Monitor" -> "Heap-Dump".

0
rogerdpack

Vielleicht jcmd ?

Das Dienstprogramm Jcmd wird zum Senden von Diagnosebefehlsanforderungen an die JVM verwendet. Diese Anforderungen sind nützlich, um Java Flight Recordings zu steuern, Fehler zu beheben und JVM- und Java-Anwendungen zu diagnostizieren.

Das jcmd-Tool wurde mit Oracle Java 7 eingeführt und ist besonders nützlich bei der Behebung von Problemen mit JVM-Anwendungen, indem es die IDs von Java-Prozessen (ähnlich wie JPS), Heap-Dumps (ähnlich wie JMAP) und Thread-Dumps (ähnlich wie JSTACK) ermittelt ), Anzeigen von Merkmalen der virtuellen Maschine wie Systemeigenschaften und Befehlszeilenflags (ähnlich wie bei jinfo) und Abrufen von Garbage Collection-Statistiken (ähnlich wie bei jstat). Das Tool jcmd wurde als "Schweizer Taschenmesser zur Untersuchung und Lösung von Problemen mit Ihrer JVM-Anwendung" und als "verstecktes Juwel" bezeichnet.

Hier ist der Prozess, den Sie zum Aufrufen der jcmd verwenden müssen:

  1. Gehe zu jcmd <pid> GC.heap_dump <file-path>
  2. In welchem
  3. pid: ist eine Java-Prozess-ID, für die der Heap-Dump erfasst wird
  4. dateipfad: ist ein Dateipfad, in dem der Heap-Dump gedruckt wird.

Weitere Informationen zum Ausführen von Java-Heapspeicherauszügen finden Sie hier.

0
Stas

Wenn Sie die Konsole/das Terminal aus irgendeinem Grund nicht verwenden können (wollen oder wollen), gibt es eine alternative Lösung. Sie können die Java-Anwendung dazu bringen, den Thread-Dump für Sie auszudrucken. Der Code, der das Stack-Trace erfasst, ist relativ einfach und kann an eine Schaltfläche oder eine Weboberfläche angehängt werden.

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

Diese Methode gibt einen String zurück, der so aussieht:

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)

Für diejenigen, die an einer Java 8-Version mit Streams interessiert sind, ist der Code noch kompakter:

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

Sie können diesen Code einfach testen mit:

System.out.print(getThreadDump());
0
HugoTeixeira

Im Folgenden wird Java-Code verwendet, um den Heap-Dump eines Java-Prozesses durch Angabe der PID abzurufen. Das Programm verwendet eine Remote-JMX-Verbindung, um den Heap zu sichern. Es kann für jemanden hilfreich sein.

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

}

0