web-dev-qa-db-fra.com

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

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

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

Existe-t-il un autre moyen de récupérer ce tas?

213
macgreg

Vous pouvez utiliser jmap pour obtenir un dump de tout processus en cours d'exécution, en supposant que vous connaissiez pid.

Utilisez le gestionnaire de tâches ou le moniteur de ressources pour obtenir la variable pid. ensuite

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

pour obtenir le tas pour ce processus.

349
rkaganda

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

Thread Dump = traces de pile pour chaque thread de la sortie de la machine virtuelle Java vers stdout sous forme de 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+BREAK si votre machine virtuelle Java est le processus de premier plan est le moyen le plus simple. Si vous avez un shell de type Unix sous Windows, tel que Cygwin ou MobaXterm, vous pouvez utiliser kill -3 {pid} comme vous le feriez sous Unix.

Pour effectuer un vidage de threads sous Unix, CTRL+C si votre machine virtuelle Java est le processus de premier plan ou que kill -3 {pid} fonctionnera tant que vous obtiendrez le bon PID pour la machine virtuelle Java.

Quelle que soit la plate-forme, Java est livré avec plusieurs utilitaires pouvant vous aider. jstack {pid} est votre meilleur choix pour les vidages de threads. http://docs.Oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Juste pour terminer la question sur le 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 recommandé de définir -D sur la ligne de commande Java afin que le vidage de segment de mémoire soit généré automatiquement lors d'une erreur OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError. Toutefois, vous pouvez également déclencher manuellement un vidage de segment de mémoire. Le moyen le plus courant consiste à utiliser l'utilitaire Java jmapname__.

REMARQUE: cet utilitaire n'est pas disponible sur toutes les plateformes. À partir de JDK 1.6, jmapest 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/

110
Derek

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

29
Roberto Flores

En plus d'utiliser le fichier jconsole/visualvm mentionné, vous pouvez utiliser jstack -l <vm-id> dans une autre fenêtre de ligne de commande et capturer ce résultat.

Le <vm-id> peut être trouvé à l'aide du gestionnaire de tâches (il s'agit de l'ID de processus sous Windows et unix) ou de jps.

jstack et jps sont tous deux inclus dans le JDK Sun version 6 et ultérieure.

17
ankon

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

16
Lawrence Dol

Essayez l'une des options ci-dessous.

  1. Pour la JVM 32 bits:

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

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

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

Question SE liée: Erreur de mémoire mémoire Java avec la commande jmap: Premature EOF

Jetez un oeil sur les différentes options de jmap à cet article article

14
Ravindra babu

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

c.f. Page de référence des options JVM

12
Daniel Winterstein

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

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

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.

6
Steve Kuo

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

5
krosenvold

Vous devez rediriger la sortie du second exécutable Java vers un fichier . Ensuite, utilisez SendSignal to send "-3" vers votre second processus.

4
Yoni Roit

Si vous utilisez JDK 1.6 ou une version ultérieure, vous pouvez utiliser la commande jmap pour effectuer un vidage de tas d'un processus Java. La condition est que vous connaissiez ProcessID.

Si vous êtes sur un ordinateur Windows, vous pouvez utiliser le Gestionnaire des tâches pour obtenir le PID. Pour les machines Linux, vous pouvez utiliser une variété de commandes telles que ps -A | grep Java ou netstat -tupln | grep Java ou top | grep Java, selon votre application.

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

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

3
Badal

J'ai écrit un petit script batch pour Java 8 (à l'aide de PsExec et jcmd) nommé jvmdump.bat , qui exporte 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 de l'utilisateur qui a lancé la machine virtuelle. Si vous vous connectez via Remote Desktop, vous devrez peut-être 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

Ceci 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 de l'autre session à partir de laquelle vous pouvez exécuter le script jvmdump.bat.

2
isapir

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 qu'elle se trouve sur la machine distante), exécutez jstatd sur la machine distante, puis supposez que vous avez une connexion directe ajoutez-le en tant qu '"hôte distant" dans visualvm, double-cliquez sur le nom de l'hôte et toutes les autres machines virtuelles Java de cette boîte s'afficheront 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".

0
rogerdpack

Peut-être que jcmd ?

Jcmd utility est utilisé pour envoyer des demandes de commande de diagnostic à la machine virtuelle Java, qui 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 Java 7 d’Oracle et est particulièrement utile pour résoudre les problèmes liés aux applications JVM en l’identifiant des ID de processus Java (apparenté à jps), en acquérant des copies de tas (apparentées à jmap), en acquérant des vidages de threads (apparentés à jstack). ), afficher les caractéristiques de la machine virtuelle, telles que les propriétés système et les indicateurs de ligne de commande (apparentés à jinfo), et acquérir des statistiques de récupération de place (ressemblant à jstat). L'outil jcmd a été appelé "un couteau suisse pour enquêter et résoudre les problèmes liés à votre application JVM" et un "joyau caché".

Voici le processus que vous devrez utiliser pour appeler la variable jcmd:

  1. Aller au 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. chemin_fichier: est un chemin de fichier dans lequel le vidage de segment de mémoire doit être imprimé.

Consultez-le pour plus d'informations sur la prise Java heap dump .

0
Stas

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 collecte la trace de pile est relativement simple et peut être associé à 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());
0
HugoTeixeira

Le code Java ci-dessous est utilisé pour obtenir le vidage de mémoire 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;
}

}

0