Comment obtenir un thread et un tas de dump d'un processus Java sur Windows qui ne tourne pas dans une console
j'ai une application Java que j'ai exécuté à partir d'une console qui exécute un autre processus Java. Je veux obtenir un fil/tas dump de ce processus enfant.
sur Unix, je pouvais faire un kill -3 <pid>
mais sur Windows AFAIK la seule façon d'obtenir un dump thread est Ctrl-Break dans la console. Mais ça ne me donne que le processus parent, pas l'enfant.
y a-t-il un autre moyen d'obtenir ce tas de ferraille?
15 réponses
vous pouvez utiliser jmap
pour obtenir un dump de n'importe quel processus en cours, en supposant que vous connaissez le pid
.
utilisez le Gestionnaire de tâches ou le moniteur de ressources pour obtenir le pid
. Puis
jmap -dump:format=b,file=cheap.bin <pid>
pour obtenir le tas pour ce processus.
vous confondez deux Java dumps différents. kill -3
génère un dump de thread, pas un dump de tas.
Thread dump = les traces de pile pour chaque thread dans la JVM de sortie sur la sortie standard sous forme de texte.
tas dump = contenu de mémoire pour la sortie du processus JVM vers un fichier binaire.
Pour prendre un thread dump sur Windows, CTRL + PAUSE si votre JVM est le processus de premier plan est la manière la plus simple. Si vous avez un shell de type unix sur des fenêtres comme Cygwin ou MobaXterm, vous pouvez utiliser kill -3 {pid}
comme vous pouvez dans Unix.
Pour prendre un thread dump dans Unix, CTRL + C si votre JVM est le processus au premier plan ou kill -3 {pid}
travailler aussi longtemps que vous obtenez le droit de PID pour la JVM.
avec l'une ou l'autre plate-forme, Java est livré avec plusieurs utilitaires qui peuvent aider. Pour les dumps de fil, jstack {pid}
est votre meilleur pari. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html
juste pour finir le dump question out: tas dumps ne sont pas couramment utilisés parce qu'ils sont difficiles à interpréter. Mais, ils ont beaucoup d'informations utiles, si vous savez où et comment regarder. L'usage le plus courant est de localiser les fuites de mémoire. C'est une bonne pratique de définir le -D
sur le java ligne de commande de sorte que le tas dump est généré automatiquement sur une OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError
mais, vous pouvez déclencher manuellement un tas dump, aussi. La plus courante est d'utiliser l'utilitaire java jmap
.
NOTE: cet utilitaire n'est pas disponible sur toutes les plateformes. Depuis JDK 1.6, jmap
est disponible sous Windows.
un exemple de ligne de commande ressemblerait à quelque chose comme
jmap -dump:file=myheap.bin {pid of the JVM}
La sortie "myheap.bin" n'est pas lisible par l'homme (pour la plupart d'entre nous), et vous aurez besoin d'un outil pour l'analyser. Ma préférence est MAT. http://www.eclipse.org/mat /
je pense que la meilleure façon de créer .le fichier HPROF dans le processus Linux est avec la commande jmap . Par exemple: jmap -dump:format=b,file=filename.hprof {PID}
en plus d'utiliser le jconsole/visualvm mentionné, vous pouvez utiliser jstack -l <vm-id>
sur une autre fenêtre de ligne de commande, et capturer cette sortie.
le jps
.
les deux jstack
et jps
sont inclus dans la version 6 et plus de Sun JDK.
je recommande le VisualVM Java distribué avec le JDK (jvisualvm.EXE.) Il peut se connecter dynamiquement et accéder aux threads et tas. J'ai trouvé précieuse pour certains problèmes.
Essayez l'une des options ci-dessous.
-
Pour la version 32 bits de la JVM:
jmap -dump:format=b,file=<heap_dump_filename> <pid>
-
Pour la version 64 bits de la JVM (explicitement citer):
jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
-
Pour la version 64 bits de la JVM avec G1GC l'algorithme dans les paramètres de la VM (Uniquement les objets vivants tas est généré avec G1GC algorithme):
jmap -J-d64 -dump:live,format=b,file=<heap_dump_filename> <pid>
Liées SE question: Java heap dump de l'erreur avec jmap commande : Prématuré EOF
Ont un oeil à des options différentes de jmap
à ce article
si vous voulez un heapdump sur out-of-memory, vous pouvez démarrer Java avec l'option -XX:-HeapDumpOnOutOfMemoryError
si vous êtes sur le serveur-jre 8 et plus, vous pouvez utiliser ceci:
jcmd PID GC.heap_dump /tmp/dump
vous pouvez lancer jconsole
(inclus avec le SDK de Java 6) puis vous connecter à votre application Java. Il vous montrera chaque fil courant et sa trace de pile.
vous pouvez envoyer le kill -3 <pid>
de Cygwin. Vous devez utiliser les options de Cygwin ps
pour trouver des processus windows puis juste envoyer le signal à ce processus.
vous devez rediriger la sortie du second exécutable java vers un fichier. Ensuite, utilisez SendSignal pour envoyer" -3 " à votre deuxième processus.
si vous utilisez JDK 1.6 ou plus, vous pouvez utiliser la commande jmap
pour faire un tas de Dump D'un processus Java, condition est que vous devriez connaître ProcessID.
si vous êtes sur Windows Machine, vous pouvez utiliser le Gestionnaire des tâches pour obtenir le PID. Pour les machines Linux, vous pouvez utiliser des types de commandes comme ps -A | grep java
ou netstat -tupln | grep java
ou top | grep java
, cela dépend de votre application.
alors vous pouvez utiliser la commande comme jmap -dump:format=b,file=sample_heap_dump.hprof 1234
où 1234 est PID.
il existe des variétés de outil disponible pour interpréter le fichier hprof. Je recommande l'outil visualvm D'Oracle, qui est simple à utiliser.
j'ai écrit un petit script par lots pour Java 8 (en utilisant PsExec
et jcmd
) nommé jvmdump.bat
, qui se débarrasse des threads, tas, propriétés du système, et 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%
il doit être exécuté dans la même session Windows de l'utilisateur qui a démarré la JVM, donc si vous vous connectez par le bureau distant vous pourriez avoir besoin de lancer une invite de commande dans Session 0
et l'exécuter à partir de là. par exemple
%PsExec% -s -h -d -i 0 cmd.exe
cela vous invitera (cliquez sur l'icône de la barre des tâches en bas) à View the message
dans la session interactive, ce qui vous mènera à la nouvelle console dans l'autre session à partir de laquelle vous pouvez exécuter le script jvmdump.bat
.
si vous ne pouvez pas (ou ne voulez pas) utiliser la console/terminal pour une raison quelconque, il existe une solution alternative. Vous pouvez faire imprimer L'application Java le thread dump pour vous. Le code qui collecte 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 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.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 s'intéressent à une version Java 8 avec streams, le 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());
sur un Oracle JDK, nous avons une commande appelée jmap (disponible dans le dossier bin de Java Home). l'utilisation de la commande est la suivante
jmap (option) (pid)
Exemple: jmap -dump:live,format=b,fichier=tas.bin (pid)