Come ottenere un dump di thread e heap di un processo Java su Windows che non è in esecuzione in una console


232

Ho un'applicazione Java che eseguo da una console che a sua volta esegue un altro processo Java. Voglio ottenere un dump di thread / heap di quel processo figlio.

Su Unix, potrei fare un kill -3 <pid>ma su Windows AFAIK l'unico modo per ottenere un dump del thread è Ctrl-Break nella console. Ma questo mi dà solo il dump del processo genitore, non il bambino.

C'è un altro modo per ottenere quella discarica dell'heap?


Risposte:


376

È possibile utilizzare jmapper ottenere un dump di qualsiasi processo in esecuzione, supponendo che si conosca il pid.

Utilizzare Task Manager o Monitor risorse per ottenere il pid. Poi

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

per ottenere l'heap per quel processo.


jmap non è disponibile per JDK5 in Windows. C'è un modo per fare il dump con JDK5 su Windows?
Santron Manibharathi,

173
Questo thread è diventato così popolare che ho appena sentito qualcuno riferirsi a una discarica di heap come "cheap.bin"
mjaggard

7
Un nome di file più semplice: "heap.hprof", in quanto è in formato HPROF.
MGM

1
Assicurati di utilizzare l'utente corretto che ha avviato il processo Java. Nel mio caso era tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump: format = b, file = <nomefile> <pid>
bitsabhi

115

Stai confondendo due diverse discariche Java. kill -3genera un dump del thread, non un dump dell'heap.

Thread dump = impila le tracce per ogni thread nell'output JVM su stdout come testo.

Dump dell'heap = contenuto della memoria per l'output del processo JVM in un file binario.

Per eseguire un dump di thread su Windows, CTRL+ BREAKse la tua JVM è il processo in primo piano è il modo più semplice. Se hai una shell unix-like su Windows come Cygwin o MobaXterm, puoi usarla kill -3 {pid}come puoi in Unix.

Per eseguire un dump di thread in Unix, CTRL+ Cse la tua JVM è il processo in primo piano o kill -3 {pid}funzionerà finché avrai il PID giusto per la JVM.

Con entrambe le piattaforme, Java viene fornito con diverse utilità che possono aiutare. Per le discariche di thread, jstack {pid}è la soluzione migliore. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Solo per finire la domanda sul dump: i dump dell'heap non sono comunemente usati perché sono difficili da interpretare. Tuttavia, contengono molte informazioni utili se sai dove / come guardarle. L'uso più comune è individuare perdite di memoria. È buona norma impostare la -Driga di comando java in modo che il dump dell'heap venga generato automaticamente su un OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError ma è anche possibile attivare manualmente un dump dell'heap. Il modo più comune è utilizzare l'utilità java jmap.

NOTA: questa utility non è disponibile su tutte le piattaforme. A partire da JDK 1.6, jmapè disponibile su Windows.

Un esempio di riga di comando sarebbe simile

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

L'output "myheap.bin" non è leggibile dall'uomo (per la maggior parte di noi) e sarà necessario uno strumento per analizzarlo. La mia preferenza è MAT. http://www.eclipse.org/mat/


3
Sul mio Linux Ctrl-C lo interrompe (lo termina), faccio Ctrl- \
nafg

Considera questo e il suo impatto generale su "Per eseguire il dump di thread su Windows, CTRL + BREAK". In realtà dipende dalla decisione ingegneristica del produttore. FE, Lenova, IIRC, è cntrl + fn + p.
ChiefTwoPencils

30

Penso che il modo migliore per creare il file .hprof nel processo Linux sia con il comando jmap . Per esempio:jmap -dump:format=b,file=filename.hprof {PID}


19

Oltre a utilizzare la menzionata jconsole / visualvm, è possibile utilizzare jstack -l <vm-id>un'altra finestra della riga di comando e acquisire tale output.

<vm-id> può essere trovato usando il task manager (è l'id di processo su windows e unix), oppure usando jps.

Entrambi jstacke jpssono inclusi in Sun JDK versione 6 e successive.


Questi strumenti non sono supportati in Java 1.6. Java 1.6 ha solo jconsole.
Vanchinathan Chandrasekaran,

7
Potresti mescolare JDK e JRE, ho esplicitamente menzionato JDK. Consulta la documentazione per gli strumenti: download.oracle.com/javase/6/docs/technotes/tools/share/… e download.oracle.com/javase/6/docs/technotes/tools/share/…
ankon

17

Raccomando Java VisualVM distribuito con JDK (jvisualvm.exe). Può connettersi in modo dinamico e accedere ai thread e all'heap. Ho trovato inestimabile per alcuni problemi.


2
Questo è il più delle volte non fattibile in quanto ha un sovraccarico attaccato ad esso e le discariche di thread vengono generalmente recuperate dalle macchine di produzione.
Hammad Dar,

la domanda originale riguarda un processo "non runnin". È probabile che jvisualvm non riesca a connettersi.
Jaberino,

3
@Jaberino: No, si tratta di un processo Java attualmente in esecuzione, in Windows, senza console associata.
Lawrence Dol,

Nelle ultime versioni Java Java VisualVM è stato sostituito da JMC / JFR . Vedi anche Quali sono le differenze tra JVisualVM e Java Mission Control?
Vadzim,

16

Se sei su server-jre 8 e versioni successive puoi usare questo:

jcmd PID GC.heap_dump /tmp/dump

1
Nella maggior parte dei sistemi di produzione, abbiamo solo jre e non jdk. Quindi questo aiuta.
Pragalathan M

15

Prova una delle seguenti opzioni.

  1. Per JVM a 32 bit:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
  2. Per JVM a 64 bit (quotazione esplicita):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
  3. Per JVM a 64 bit con algoritmo G1GC nei parametri VM (viene generato solo l'heap di oggetti live con l'algoritmo G1GC):

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

Domanda SE relativa: errore di dump dell'heap Java con comando jmap: EOF precoce

Dai un'occhiata a varie opzioni di jmapquesto articolo


13

Se si desidera un heapdump con memoria insufficiente, è possibile avviare Java con l'opzione -XX:-HeapDumpOnOutOfMemoryError

vedi pagina di riferimento Opzioni JVM


Grazie Daniel. Dove viene creato questo file su un computer Windows? Esiste un percorso predefinito?
lava,

1
@lava È possibile impostare il percorso tramite -XX: HeapDumpPath, come descritto nella pagina Opzioni VM di Oracle .
Kamczak,

Eccezionale. Volevo eseguire un test durante la notte nella speranza di mostrare una perdita di memoria, ma ero preoccupato per OOM e crashign mentre non sono presente. Questo è perfetto.
Basilio,

7

È possibile eseguire jconsole(incluso con l'SDK di Java 6), quindi connettersi all'applicazione Java. Ti mostrerà ogni thread in esecuzione e la sua traccia dello stack.


la migliore risposta di gran lunga! Non lo sapevo fino ad ora ed è davvero pratico!
Xerus,

7

Puoi inviarlo kill -3 <pid>da Cygwin. Devi usare le psopzioni di Cygwin per trovare i processi di Windows, quindi inviare il segnale a quel processo.



3

Se si utilizza JDK 1.6 o versioni successive, è possibile utilizzare jmap comando per eseguire un dump dell'heap di un processo Java, condizione che si dovrebbe conoscere ProcessID.

Se sei su un computer Windows, puoi utilizzare Task Manager per ottenere il PID. Per macchine Linux puoi usare varietà di comandi come ps -A | grep javao netstat -tupln | grep javaotop | grep java , dipende dalla tua applicazione.

Quindi è possibile utilizzare il comando come jmap -dump:format=b,file=sample_heap_dump.hprof 1234dove 1234 è PID.

Ci sono varietà di strumenti disponibili per interpretare il file hprof. Consiglierò lo strumento visualvm di Oracle, che è semplice da usare.


3

Se non è possibile (o non si desidera) utilizzare la console / il terminale per qualche motivo, esiste una soluzione alternativa. Puoi fare in modo che l'applicazione Java stampi il dump del thread per te. Il codice che raccoglie Stack Trace è ragionevolmente semplice e può essere collegato a un pulsante o un'interfaccia web.

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

Questo metodo restituirà una stringa simile al seguente:

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)

Per coloro che sono interessati a una versione Java 8 con stream, il codice è ancora più compatto:

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

Puoi facilmente testare questo codice con:

System.out.print(getThreadDump());

3

Il seguente script utilizza PsExec per connettersi a un'altra sessione di Windows in modo che funzioni anche quando connesso tramite il servizio Desktop remoto.

Ho scritto un piccolo script batch per Java 8 (usando PsExece jcmd) chiamato jvmdump.bat, che scarica i thread, l'heap, le proprietà di sistema e gli argomenti JVM.

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

Deve essere eseguito nella stessa sessione Windows dell'utente che ha avviato la JVM, quindi se ti connetti tramite Desktop remoto potrebbe essere necessario avviare un prompt dei comandi Session 0ed eseguirlo da lì. per esempio

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

Questo ti chiederà (fai clic sull'icona della barra delle applicazioni in basso) a View the messagenella sessione interattiva, che ti porterà alla nuova console nell'altra sessione da cui puoi eseguire lo jvmdump.batscript.


2

Come ottenere l'ID di processo dell'applicazione Java?

Eseguire il comando 'jcmd' per ottenere l'id di processo delle applicazioni java.

Come ottenere il dump del thread?

jcmd PID Thread.print> thread.dump

Link di riferimento

Puoi anche usare jstack per ottenere il dump del thread (jstack PID> thread.dump). Link di riferimento

Come ottenere la discarica dell'heap?

Utilizzare lo strumento jmap per ottenere il dump dell'heap. jmap -F -dump: live, format = b, file = heap.bin PID

PID sta per ID di processo dell'applicazione. Link di riferimento


1

Forse jcmd ?

L' utilità Jcmd viene utilizzata per inviare richieste di comandi diagnostici a JVM, dove queste richieste sono utili per il controllo delle registrazioni di volo Java, la risoluzione dei problemi e la diagnosi di applicazioni JVM e Java.

Lo strumento jcmd è stato introdotto con Java 7 di Oracle ed è particolarmente utile nella risoluzione dei problemi con le applicazioni JVM utilizzandolo per identificare gli ID dei processi Java (simile a jps), acquisire dump dell'heap (simile a jmap), acquisire dump del thread (simile a jstack ), visualizzando le caratteristiche della macchina virtuale come le proprietà di sistema e i flag della riga di comando (simili a jinfo) e acquisendo le statistiche di garbage collection (simili a jstat). Lo strumento jcmd è stato chiamato "un coltellino svizzero per indagare e risolvere i problemi con l'applicazione JVM" e un "gioiello nascosto".

Ecco il processo che dovrai usare per invocare jcmd:

  1. Vai a jcmd <pid> GC.heap_dump <file-path>
  2. In quale
  3. pid: è un ID processo Java, per il quale verrà catturato il dump dell'heap
  4. file-path: è un percorso di file in cui viene stampato il dump dell'heap.

Dai un'occhiata per ulteriori informazioni su come eseguire il dump dell'heap Java .


0

Follow-up di Visualvm:

Se "non riesci a connetterti" alla tua JVM in esecuzione da jvisualvm perché non l'hai avviata con gli argomenti JVM giusti (ed è sulla scatola remota), esegui jstatdsulla scatola remota, quindi, supponendo che tu abbia una connessione diretta, aggiungi come "host remoto" in visualvm, fai doppio clic sul nome host e tutte le altre JVM su quella casella appariranno magicamente in visualvm.

Se non si dispone di "connessione diretta" alle porte su quella casella, è possibile farlo anche tramite un proxy .

Una volta che puoi vedere il processo che desideri, esegui il drill-in in jvisualvm e usa la scheda monitor -> pulsante "heapdump".


0

Sotto il codice java viene utilizzato per ottenere l'heap dump di un processo Java fornendo PID. Il programma utilizza la connessione JMX remota per scaricare l'heap. Potrebbe essere utile per qualcuno.

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

Per poter eseguire il dump del thread / dump dell'heap da un processo java figlio in Windows, è necessario identificare l'ID del processo figlio come primo passo.

Emettendo il comando: jps sarai in grado di ottenere tutti gli ID di processo java in esecuzione sul tuo computer Windows. Da questo elenco è necessario selezionare l'ID processo figlio. Una volta che hai l'ID del processo figlio, ci sono varie opzioni per acquisire il dump del thread e il dump dell'heap.

Catturare discariche di thread:

Esistono 8 opzioni per acquisire i dump di thread:

  1. jstack
  2. uccidere -3
  3. jvisualvm
  4. JMC
  5. Windows (Ctrl + Break)
  6. ThreadMXBean
  7. Strumenti APM
  8. jcmd

I dettagli su ciascuna opzione sono disponibili in questo articolo . Dopo aver acquisito i dump dei thread, puoi utilizzare strumenti come fastThread , Samuraito analizza i dump dei thread.

Catturare i dump dell'heap:

Esistono 7 opzioni per acquisire i dump dell'heap:

  1. jmap

  2. -XX: + HeapDumpOnOutOfMemoryError

  3. jcmd

  4. jvisualvm

  5. JMX

  6. Approccio programmatico

  7. Console amministrative

I dettagli su ciascuna opzione sono disponibili in questo articolo . Dopo aver acquisito il dump dell'heap, è possibile utilizzare strumenti come lo strumento Eclipse Memory Analysis , HeapHero per analizzare i dump dell'heap acquisiti.


-1

Su un Oracle JDK, abbiamo un comando chiamato jmap (disponibile nella cartella bin di Java Home). l'utilizzo del comando è il seguente

jmap (opzione) (pid)

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.