Comment obtenir un vidage de thread et de tas d'un processus Java sous Windows qui ne s'exécute pas dans une console

232

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

Sous Unix, je pourrais faire un kill -3 <pid>mais sous Windows AFAIK le seul moyen d'obtenir un vidage de thread est Ctrl-Break dans la console. Mais cela ne me donne que la décharge du processus parental, pas l'enfant.

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

Kasun Siyambalapitiya
la source

Réponses:

376

Vous pouvez utiliser jmappour obtenir un vidage de tout processus en cours d'exécution, en supposant que vous connaissez le pid.

Utilisez le Gestionnaire des tâches ou le Moniteur de ressources pour obtenir le pid. ensuite

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

pour obtenir le tas pour ce processus.

rkaganda
la source
jmap n'est pas disponible pour JDK5 dans Windows. Existe-t-il un moyen de faire un vidage avec JDK5 sur Windows?
Santron Manibharathi
173
Ce fil est devenu si populaire que je viens d'entendre quelqu'un parler d'un vidage de tas comme d'un "cheap.bin"
mjaggard
7
Un nom de fichier plus simple: "heap.hprof", car il est au format HPROF.
MGM
1
Assurez-vous d'utiliser le bon utilisateur qui a lancé le processus java. Dans mon cas, c'était tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump: format = b, file = <nomfichier> <pid>
bitsabhi
115

Vous confondez deux vidages java différents. kill -3génère un vidage de thread, pas un vidage de tas.

Dump de thread = traces de pile pour chaque thread de la sortie JVM vers stdout en tant que texte.

Heap dump = contenu de la mémoire pour la sortie du processus JVM dans un fichier binaire.

Pour effectuer un vidage de thread sous Windows, CTRL+ BREAKsi votre JVM est au premier plan, le processus est le plus simple. Si vous avez un shell de type Unix sur Windows comme Cygwin ou MobaXterm, vous pouvez l'utiliser kill -3 {pid}comme vous pouvez le faire sous Unix.

Pour effectuer un vidage de threads sous Unix, CTRL+ Csi votre JVM est le processus de premier plan ou kill -3 {pid}fonctionnera tant que vous obtenez le bon PID pour la JVM.

Avec l'une ou l'autre plate-forme, Java est fourni avec plusieurs utilitaires qui peuvent vous aider. Pour les vidages de fil, jstack {pid}c'est votre meilleur pari. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Juste pour terminer la question du vidage: les vidages de tas ne sont pas couramment utilisés car ils sont difficiles à interpréter. Mais, ils contiennent beaucoup d'informations utiles si vous savez où / comment les regarder. L'utilisation la plus courante consiste à localiser les fuites de mémoire. Il est -Drecommandé de définir le sur la ligne de commande java afin que le vidage de segment soit généré automatiquement lors d'une OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError mais vous pouvez également déclencher manuellement un vidage de segment . La manière la plus courante consiste à utiliser l'utilitaire java jmap.

REMARQUE: cet utilitaire n'est pas disponible sur toutes les plateformes. Depuis JDK 1.6, jmapest disponible sur Windows.

Un exemple de ligne de commande ressemblerait à quelque chose

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/

Derek
la source
3
Sur mon Linux Ctrl-C interrompt (termine), je fais Ctrl- \
nafg
Considérez ceci et son impact général sur «Pour effectuer un vidage de thread sur Windows, CTRL + PAUSE». Cela dépend en fait de la décision technique du fabricant. FE, Lenova, IIRC, est cntrl + fn + p.
ChiefTwoPencils
30

Je pense que la meilleure façon de créer un fichier .hprof dans un processus Linux est avec la commande jmap . Par exemple:jmap -dump:format=b,file=filename.hprof {PID}

Roberto Flores
la source
19

En plus d'utiliser la jconsole / visualvm mentionnée, vous pouvez utiliser jstack -l <vm-id> sur une autre fenêtre de ligne de commande et capturer cette sortie.

Le <vm-id> peut être trouvé en utilisant le gestionnaire de tâches (c'est l'identifiant du processus sur Windows et Unix), ou en utilisant jps .

Les deux jstacket jpssont inclus dans la version 6 et supérieure de Sun JDK.

ankon
la source
Ces outils ne sont pas pris en charge dans Java 1.6. Java 1.6 n'a que jconsole.
Vanchinathan Chandrasekaran
7
Vous mélangez peut-être JDK et JRE, j'ai mentionné explicitement JDK. Voir la documentation des outils: download.oracle.com/javase/6/docs/technotes/tools/share/… et download.oracle.com/javase/6/docs/technotes/tools/share/…
ankon
17

Je recommande Java VisualVM distribué avec le JDK (jvisualvm.exe). Il peut se connecter dynamiquement et accéder aux threads et au tas. Je l'ai trouvé inestimable pour certains problèmes.

Lawrence Dol
la source
2
C'est la plupart du temps impossible car il y a une surcharge attachée et les vidages de threads sont généralement récupérés des machines de production.
Hammad Dar
la question d'origine concerne un processus «non-runnin». Il est probable que jvisualvm ne puisse pas se connecter.
Jaberino
3
@Jaberino: Non, il s'agit d'un processus Java en cours d'exécution, sous Windows, sans console associée.
Lawrence Dol
Dans les dernières versions de Java, Java VisualVM a été remplacé par JMC / JFR . Voir aussi Quelles sont les différences entre JVisualVM et Java Mission Control?
Vadzim
16

Si vous êtes sur server-jre 8 et supérieur, vous pouvez utiliser ceci:

jcmd PID GC.heap_dump /tmp/dump
Atul Soman
la source
1
Dans la plupart des systèmes de production, nous n'avons que jre et non jdk. Donc ça aide.
Pragalathan M
15

Essayez l'une des options ci-dessous.

  1. Pour JVM 32 bits:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
  2. Pour JVM 64 bits (en citant explicitement):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
  3. Pour la machine virtuelle Java 64 bits avec l'algorithme G1GC dans les paramètres de la machine virtuelle (seul le tas d'objets en direct est généré avec l'algorithme G1GC):

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

Question SE connexe: erreur de vidage de tas Java avec la commande jmap: EOF prématuré

Jetez un œil aux différentes options de jmapcet article

Ravindra babu
la source
13

Si vous voulez un heapdump sur mémoire insuffisante, vous pouvez démarrer Java avec l'option -XX:-HeapDumpOnOutOfMemoryError

cf Page de référence des options JVM

Daniel Winterstein
la source
Merci Daniel. Où ce fichier est-il créé sur une machine Windows? Existe-t-il un chemin par défaut?
lave
1
@lava Vous pouvez définir le chemin via -XX: HeapDumpPath, comme décrit sur la page Options VM d'Oracle .
kamczak
Impressionnant. Je voulais effectuer un test pendant la nuit dans l'espoir de montrer une fuite de mémoire mais j'étais inquiet pour le MOO et le crashign pendant que je ne suis pas présent. C'est parfait.
Basil
7

Vous pouvez exécuter jconsole(inclus avec le SDK de Java 6) puis vous connecter à votre application Java. Il vous montrera chaque thread en cours d'exécution et sa trace de pile.

Steve Kuo
la source
la meilleure réponse de loin! Je ne le savais pas jusqu'à présent et c'est vraiment pratique!
Xerus
7

Vous pouvez envoyer le kill -3 <pid>depuis Cygwin. Vous devez utiliser les psoptions Cygwin pour trouver les processus Windows, puis envoyer simplement le signal à ce processus.

krosenvold
la source
3

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

Si vous êtes sur Windows Machine, vous pouvez utiliser le Gestionnaire des tâches pour obtenir le PID. Pour la machine Linux, vous pouvez utiliser des variétés de commandes comme ps -A | grep javaou netstat -tupln | grep javaou top | grep java, selon votre application.

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

Il existe différentes variétés d' outils pour interpréter le fichier hprof. Je recommanderai l'outil visualvm d'Oracle, qui est simple à utiliser.

Badal
la source
3

Si vous ne pouvez pas (ou ne voulez pas) utiliser la console / le terminal pour une raison quelconque, il existe une solution alternative. Vous pouvez faire en sorte que l'application Java imprime le vidage de thread pour vous. Le code qui recueille la trace de pile est relativement 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 retournera 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$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 intéressés par une version 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());
HugoTeixeira
la source
3

Le script suivant utilise PsExec pour se connecter à une autre session Windows afin qu'il fonctionne même lorsqu'il est connecté via le service Bureau à distance.

J'ai écrit un petit script batch pour Java 8 (en utilisant PsExecet jcmd) nommé jvmdump.bat, qui vide les threads, le tas, les propriétés système et les arguments 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 démarré la machine virtuelle Java. Par conséquent, si vous vous connectez via Remote Desktop, vous devrez peut-être lancer une invite de commande Session 0et 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 messagedans la session interactive, qui vous amènera à la nouvelle console dans l'autre session à partir de laquelle vous pouvez exécuter le jvmdump.batscript.

isapir
la source
2

Comment obtenir l'ID de processus de l'application Java?

Exécutez la commande «jcmd» pour obtenir l'ID de processus des applications java.

Comment obtenir un vidage de thread?

jcmd PID Thread.print> thread.dump

Lien de référence

Vous pouvez même utiliser jstack pour obtenir un vidage de thread (jstack PID> thread.dump). Lien de référence

Comment obtenir le vidage de tas?

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

PID signifie id de processus de l'application. Lien de référence

Hari Krishna
la source
1

Peut-être jcmd ?

L' utilitaire Jcmd est utilisé pour envoyer des demandes de commande de diagnostic à la JVM, où ces demandes sont utiles pour contrôler les enregistrements de vol Java, dépanner et diagnostiquer les applications JVM et Java.

L'outil jcmd a été introduit avec Oracle 7 Java et est particulièrement utile pour résoudre les problèmes avec les applications JVM en l'utilisant pour identifier les ID des processus Java (apparenté à jps), acquérir des vidages de tas (apparentés à jmap), acquérir des vidages de threads (apparentés à jstack ), l'affichage des caractéristiques des machines virtuelles telles que les propriétés du système et les indicateurs de ligne de commande (semblable à jinfo), et l'acquisition de statistiques de récupération de place (semblable à jstat). L'outil jcmd a été appelé "un couteau de l'armée suisse pour enquêter et résoudre les problèmes avec votre application JVM" et un "joyau caché".

Voici le processus que vous devrez utiliser pour invoquer jcmd:

  1. Aller à jcmd <pid> GC.heap_dump <file-path>
  2. Dans lequel
  3. pid: est un identifiant de processus Java, pour lequel le vidage de tas sera capturé.
  4. file-path: est un chemin de fichier dans lequel le vidage de tas est imprimé.

Consultez-le pour plus d'informations sur la prise de vidage de tas Java .

Johnny
la source
0

Suivi Visualvm:

Si vous "ne pouvez pas vous connecter" à votre machine virtuelle Java en cours d'exécution à partir de jvisualvm parce que vous ne l'avez pas démarrée avec les bons arguments de la machine virtuelle Java (et c'est sur la boîte distante), exécutez jstatdla boîte distante, puis, en supposant que vous avez une connexion directe, ajoutez comme un "hôte distant" dans visualvm, double-cliquez sur le nom d'hôte, et toutes les autres machines virtuelles Java sur cette boîte apparaîtront comme par magie dans visualvm.

Si vous n'avez pas de "connexion directe" aux ports de cette boîte, vous pouvez également le faire via un proxy .

Une fois que vous pouvez voir le processus que vous souhaitez, explorez-le dans jvisualvm et utilisez l'onglet moniteur -> bouton "heapdump".

rogerdpack
la source
0

Le code java ci-dessous est utilisé pour obtenir le vidage de tas d'un processus Java en fournissant un PID. Le programme utilise une connexion JMX distante pour vider le tas. Cela peut être utile pour quelqu'un.

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

}

Ramesh Subramanian
la source
0

Afin de prendre le vidage de thread / vidage de tas d'un processus java enfant dans Windows, vous devez identifier l'ID du processus enfant comme première étape.

En exécutant la commande: jps, vous pourrez obtenir tous les ID de processus java qui s'exécutent sur votre machine Windows. Dans cette liste, vous devez sélectionner l'ID de processus enfant. Une fois que vous avez l'ID de processus enfant, il existe différentes options pour capturer le vidage de thread et les vidages de tas.

Capture de vidages de thread:

Il existe 8 options pour capturer les vidages de threads:

  1. jstack
  2. tuer -3
  3. jvisualVM
  4. JMC
  5. Windows (Ctrl + Break)
  6. ThreadMXBean
  7. Outils APM
  8. jcmd

Des détails sur chaque option sont disponibles dans cet article . Une fois que vous avez capturé les vidages de threads, vous pouvez utiliser des outils comme fastThread , Samuraito analyser les vidages de threads.

Capture de vidages de tas:

Il existe 7 options pour capturer les vidages de tas:

  1. jmap

  2. -XX: + HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. Approche programmatique

  7. Consoles administratives

Des détails sur chaque option sont disponibles dans cet article . Une fois que vous avez capturé le vidage de tas, vous pouvez utiliser des outils comme l' outil d'analyse de mémoire Eclipse , HeapHero pour analyser les vidages de tas capturés.

Jim T
la source
-1

Sur un Oracle JDK, nous avons une commande appelée jmap (disponible dans le dossier bin de Java Home). l'utilisation de la commande se présente comme suit

jmap (option) (pid)

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

Suresh Ram
la source