259 votes

Comment obtenir un vidage des threads et du tas d'un processus Java sous Windows qui n'est pas exécuté dans une console ?

J'ai une application Java que je lance à partir d'une console qui, à son tour, exécute un autre processus Java. Je veux obtenir un vidage de thread/heap de ce processus enfant.

Sous Unix, je pouvais faire un kill -3 <pid> mais sous Windows, la seule façon d'obtenir un vidage de fil est de faire Ctrl-Break dans la console. Mais cela ne me donne que le vidage du processus parent, pas du processus enfant.

Y a-t-il un autre moyen d'obtenir ce vidage de tas ?

7voto

Yoni Roit Points 11338

Vous devez rediriger la sortie du deuxième exécutable java vers un fichier. Ensuite, utilisez SendSignal a envoyer "-3" à votre deuxième processus.

5voto

Igal Points 836

Le script suivant utilise PsExec pour se connecter à une autre session Windows afin qu'il fonctionne même lorsqu'il est connecté via Remote Desktop Service.

J'ai écrit un petit lot de script pour Java 8 (utilisant PsExec y jcmd ) nommé jvmdump.bat qui vide les threads, le tas, les propriétés du système et les arguments de la 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%

Il doit être exécuté dans la même session Windows que celle de l'utilisateur qui a lancé la JVM. Par conséquent, si vous vous connectez par l'intermédiaire du Bureau à distance, vous devrez peut-être lancer une invite de commande dans l'application Session 0 et l'exécuter à partir de là, par exemple.

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

Vous serez alors invité (en cliquant sur l'icône de la barre des tâches en bas) à View the message dans la session interactive, ce qui vous amènera à la nouvelle console dans l'autre session à partir de laquelle vous pourrez exécuter la commande jvmdump.bat script.

5voto

Hari Krishna Points 797

Comment obtenir l'identifiant de processus d'une application java ?

Exécutez la commande 'jcmd' pour obtenir l'identifiant du processus des applications java.

Comment obtenir Thread dump ?

jcmd PID Thread.print > thread.dump

Référence lien

Vous pouvez même utiliser jstack pour obtenir le dump des threads (jstack PID > thread.dump). Référence lien

Comment obtenir un dépotoir ?

Utilisez l'outil jmap pour obtenir un vidage du tas. jmap -F -dump:live,format=b,file=heap.bin PID

PID est l'identifiant du processus de l'application. Référence lien

4voto

Badal Points 552

Si vous utilisez JDK 1.6 ou supérieur, vous pouvez utiliser jmap pour prendre un heap Dump d'un processus Java, la condition est que vous devez connaître ProcessID.

Si vous êtes sur une machine Windows, vous pouvez utiliser le Gestionnaire des tâches pour obtenir le PID. Pour les machines Linux, vous pouvez utiliser diverses commandes telles que ps -A | grep java o netstat -tupln | grep java o top | grep java dépend de votre application.

Ensuite, vous pouvez utiliser la commande comme jmap -dump:format=b,file=sample_heap_dump.hprof 1234 où 1234 est le PID.

Il existe des variétés de outil disponible pour interpréter le fichier hprof. Je vous recommande l'outil visualvm d'Oracle, qui est simple à utiliser.

4voto

HugoTeixeira Points 2545

Si, pour une raison quelconque, vous ne pouvez pas (ou ne voulez pas) utiliser la console/le terminal, il existe une autre solution. Vous pouvez faire en sorte que l'application Java imprime le vidage de fil pour vous. Le code qui recueille la trace de la pile est raisonnablement simple et peut être attaché à un bouton ou à une interface 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();
}

Cette méthode renvoie une chaîne de caractères qui ressemble à ceci :

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)

Pour ceux qui sont intéressés par une version de Java 8 avec des flux, le code est encore plus 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();
}

Vous pouvez facilement tester ce code avec :

System.out.print(getThreadDump());

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X