Cómo obtener un volcado de subprocesos y heap de un proceso Java en Windows que no se ejecuta en una consola


232

Tengo una aplicación Java que ejecuto desde una consola que a su vez ejecuta otro proceso Java. Quiero obtener un volcado de hebra / montón de ese proceso secundario.

En Unix, podría hacer un kill -3 <pid>pero en Windows AFAIK la única forma de obtener un volcado de subprocesos es Ctrl-Break en la consola. Pero eso solo me da el vuelco del proceso padre, no el hijo.

¿Hay otra forma de obtener ese volcado de montón?


Respuestas:


376

Puede usar jmappara obtener un volcado de cualquier proceso en ejecución, suponiendo que conozca el pid.

Utilice el Administrador de tareas o el Monitor de recursos para obtener el pid. Luego

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

para obtener el montón para ese proceso.


jmap no está disponible para JDK5 en windows. ¿Hay alguna forma de volcar con JDK5 en Windows?
Santron Manibharathi

173
Este hilo se ha vuelto tan popular que acabo de escuchar a alguien refiriéndose a un basurero como "cheap.bin"
mjaggard

77
Un nombre de archivo más directo: "heap.hprof", ya que está en formato HPROF.
MGM

1
Asegúrese de utilizar el usuario correcto que inició el proceso de Java. En mi caso fue tomcat8 ps -C java -o pid sudo -u tomcat8 jmap -dump: format = b, file = <filename> <pid>
bitsabhi

115

Estás confundiendo dos volcados de Java diferentes. kill -3genera un volcado de subprocesos, no un volcado de almacenamiento dinámico.

Volcado de subprocesos = seguimientos de pila para cada subproceso en la salida JVM para stdout como texto

Volcado de pila = contenido de memoria para la salida del proceso JVM a un archivo binario.

Para realizar un volcado de subprocesos en Windows, CTRL+ BREAKsi su JVM es el proceso en primer plano es la forma más sencilla. Si tiene un shell similar a Unix en Windows como Cygwin o MobaXterm, puede usarlo kill -3 {pid}como puede hacerlo en Unix.

Para realizar un volcado de subprocesos en Unix, CTRL+ Csi su JVM es el proceso en primer plano o kill -3 {pid}funcionará siempre que obtenga el PID correcto para la JVM.

Con cualquier plataforma, Java viene con varias utilidades que pueden ayudar. Para volcados de hilo, jstack {pid}es su mejor apuesta. http://docs.oracle.com/javase/1.5.0/docs/tooldocs/share/jstack.html

Solo para terminar la pregunta del volcado: los volcados del montón no se usan comúnmente porque son difíciles de interpretar. Sin embargo, tienen mucha información útil si sabes dónde / cómo mirarlos. El uso más común es localizar pérdidas de memoria. Es una buena práctica establecer el -Den la línea de comandos de Java para que el volcado del montón se genere automáticamente en un OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError pero también puede activar manualmente un volcado del montón. La forma más común es usar la utilidad java jmap.

NOTA: esta utilidad no está disponible en todas las plataformas. A partir de JDK 1.6, jmapestá disponible en Windows.

Un ejemplo de línea de comando sería algo así

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

El resultado "myheap.bin" no es legible por humanos (para la mayoría de nosotros), y necesitará una herramienta para analizarlo. Mi preferencia es MAT. http://www.eclipse.org/mat/


3
En mi Linux Ctrl-C lo interrumpe (termina), hago Ctrl- \
nafg el

Considere esto y su impacto general en "Para realizar un volcado de subprocesos en Windows, CTRL + BREAK". De hecho, depende de la decisión de ingeniería del fabricante. FE, Lenova, IIRC, es cntrl + fn + p.
ChiefTwoPencils

30

Creo que la mejor manera de crear un archivo .hprof en el proceso de Linux es con el comando jmap . Por ejemplo:jmap -dump:format=b,file=filename.hprof {PID}


19

Además de usar el jconsole / visualvm mencionado, puede usarlo jstack -l <vm-id>en otra ventana de línea de comando y capturar esa salida.

El <vm-id> se puede encontrar usando el administrador de tareas (es la identificación del proceso en Windows y Unix), o usando jps.

Tanto jstacky jpsse incluirá en la versión de Sun JDK 6 y superior.


Estas herramientas no son compatibles con Java 1.6. Java 1.6 solo tiene jconsole.
Vanchinathan Chandrasekaran

77
Puede que estés mezclando JDK y JRE, mencioné explícitamente JDK. Consulte la documentación de las herramientas: download.oracle.com/javase/6/docs/technotes/tools/share/… y download.oracle.com/javase/6/docs/technotes/tools/share/…
ankon

17

Recomiendo Java VisualVM distribuido con JDK (jvisualvm.exe). Se puede conectar dinámicamente y acceder a los subprocesos y al montón. He encontrado en invaluable para algunos problemas.


2
La mayoría de las veces no es factible ya que tiene una sobrecarga adjunta y los volcados de subprocesos generalmente se recuperan de las máquinas de producción.
Hammad Dar

La pregunta original es sobre un proceso "no ejecutado". Es probable que jvisualvm no pueda conectarse.
Jaberino

3
@Jaberino: No, se trata de un proceso Java actualmente en ejecución, en Windows, sin una consola asociada.
Lawrence Dol

En las últimas versiones de Java, Java VisualVM fue reemplazado por JMC / JFR . Consulte también ¿Cuáles son las diferencias entre JVisualVM y Java Mission Control?
Vadzim

16

Si está en server-jre 8 y superior, puede usar esto:

jcmd PID GC.heap_dump /tmp/dump

1
En la mayoría de los sistemas de producción, solo tenemos jre y no jdk. Entonces esto ayuda.
Pragalathan M

15

Pruebe una de las siguientes opciones.

  1. Para JVM de 32 bits:

    jmap -dump:format=b,file=<heap_dump_filename> <pid>
  2. Para JVM de 64 bits (citando explícitamente):

    jmap -J-d64 -dump:format=b,file=<heap_dump_filename> <pid>
  3. Para JVM de 64 bits con algoritmo G1GC en parámetros VM (solo el montón de objetos vivos se genera con el algoritmo G1GC):

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

Pregunta SE relacionada: error de volcado del montón de Java con el comando jmap: EOF prematuro

Echa un vistazo a varias opciones de jmapen este artículo


13

Si desea un heapdump sin memoria, puede iniciar Java con la opción -XX:-HeapDumpOnOutOfMemoryError

cf Página de referencia de opciones de JVM


Gracias Daniel ¿Dónde se crea este archivo en una máquina con Windows? ¿Hay una ruta predeterminada?
lava

1
@lava Puede establecer la ruta a través de -XX: HeapDumpPath, como se describe en la página de Opciones de VM de Oracle .
kamczak

Increíble. Quería realizar una prueba durante la noche con la esperanza de mostrar una pérdida de memoria, pero estaba preocupado por OOM y crashign mientras no estoy presente. Esto es perfecto.
Albahaca

7

Puede ejecutar jconsole(incluido con el SDK de Java 6) y luego conectarse a su aplicación Java. Le mostrará cada subproceso en ejecución y su seguimiento de pila.


mejor respuesta por lejos! ¡No sabía esto hasta ahora y es realmente práctico!
Xerus

7

Puedes enviarlo kill -3 <pid>desde Cygwin. Debe usar las psopciones de Cygwin para buscar procesos de Windows y luego enviar la señal a ese proceso.



3

Si está utilizando JDK 1.6 o superior, puede usar el jmapcomando para realizar un volcado dinámico de un proceso Java, la condición es que debe conocer ProcessID.

Si está en una máquina con Windows, puede usar el Administrador de tareas para obtener PID. Para la máquina Linux, puede usar variedades de comandos como ps -A | grep javao netstat -tupln | grep javao top | grep java, depende de su aplicación.

Entonces puede usar el comando como jmap -dump:format=b,file=sample_heap_dump.hprof 1234donde 1234 es PID.

Existen diversas herramientas disponibles para interpretar el archivo hprof. Recomendaré la herramienta visualvm de Oracle, que es fácil de usar.


3

Si no puede (o no quiere) usar la consola / terminal por alguna razón, hay una solución alternativa. Puede hacer que la aplicación Java imprima el volcado de subprocesos por usted. El código que recopila Stack Trace es razonablemente simple y se puede adjuntar a un botón o una interfaz 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();
}

Este método devolverá una cadena que se ve así:

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)

Para aquellos interesados ​​en una versión Java 8 con transmisiones, el código es aún más compacto:

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

Puede probar fácilmente este código con:

System.out.print(getThreadDump());

3

La siguiente secuencia de comandos utiliza PsExec para conectarse a otra sesión de Windows, por lo que funciona incluso cuando se conecta a través del Servicio de escritorio remoto.

Escribí un pequeño script por lotes para Java 8 (usando PsExecy jcmd) llamado jvmdump.bat, que volca los hilos, el montón, las propiedades del sistema y los argumentos 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%

Debe ejecutarse en la misma sesión de Windows del usuario que inició la JVM, por lo que si se conecta a través de Escritorio remoto, es posible que deba iniciar un símbolo del sistema Session 0y ejecutarlo desde allí. p.ej

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

Esto le indicará (haga clic en el icono de la barra de tareas en la parte inferior) View the messageen la sesión interactiva, que lo llevará a la nueva consola en la otra sesión desde la que puede ejecutar el jvmdump.batscript.


2

¿Cómo obtener la identificación del proceso de la aplicación Java?

Ejecute el comando 'jcmd' para obtener el id de proceso de las aplicaciones java.

¿Cómo obtener el volcado de subprocesos?

jcmd PID Thread.print> thread.dump

Enlace de referencia

Incluso puede usar jstack para obtener volcado de hilo (jstack PID> thread.dump). Enlace de referencia

¿Cómo obtener el volcado del montón?

Use la herramienta jmap para obtener el volcado del montón. jmap -F -dump: live, format = b, file = heap.bin PID

PID significa id de proceso de la aplicación. Enlace de referencia


1

Tal vez jcmd ?

La utilidad Jcmd se utiliza para enviar solicitudes de comandos de diagnóstico a la JVM, donde estas solicitudes son útiles para controlar las grabaciones de vuelo de Java, solucionar problemas y diagnosticar aplicaciones JVM y Java.

La herramienta jcmd se introdujo con Java 7 de Oracle y es particularmente útil para solucionar problemas con aplicaciones JVM al usarla para identificar los ID de los procesos Java (similares a jps), adquirir volcados de montón (similares a jmap), adquirir volcados de subprocesos (similares a jstack ), ver las características de la máquina virtual, como las propiedades del sistema y los indicadores de línea de comandos (similar a jinfo), y adquirir estadísticas de recolección de basura (similar a jstat). La herramienta jcmd ha sido llamada "una navaja suiza para investigar y resolver problemas con su aplicación JVM" y una "gema oculta".

Este es el proceso que deberá utilizar para invocar jcmd:

  1. Ir jcmd <pid> GC.heap_dump <file-path>
  2. En el cual
  3. pid: es un Id. de proceso Java, para el que se capturará el volcado del montón. Además, el
  4. file-path: es una ruta de archivo en la que se imprime el volcado de almacenamiento dinámico.

Compruébelo para obtener más información sobre cómo realizar un volcado de almacenamiento dinámico de Java .


0

Seguimiento visualvm:

Si "no puede conectarse" a su JVM en ejecución desde jvisualvm porque no lo inició con los argumentos JVM correctos (y está en el cuadro remoto), ejecute jstatden el cuadro remoto y, suponiendo que tenga una conexión directa, agregue como "host remoto" en visualvm, haga doble clic en el nombre del host, y todas las demás JVM en ese cuadro aparecerán mágicamente en visualvm.

Si no tiene "conexión directa" a los puertos en ese cuadro, también puede hacerlo a través de un proxy .

Una vez que pueda ver el proceso que desea, profundice en él en jvisualvm y use la pestaña del monitor -> botón "heapdump".


0

El siguiente código de Java se utiliza para obtener el volcado del montón de un proceso Java al proporcionar PID. El programa usa una conexión JMX remota para volcar el montón. Puede ser útil para alguien.

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

Para realizar el volcado de hebras / volcado de montón de un proceso Java secundario en Windows, debe identificar el Id. Del proceso secundario como primer paso.

Al emitir el comando: jps , podrá obtener todos los ID de proceso de Java que se ejecutan en su máquina Windows. De esta lista, debe seleccionar el ID del proceso secundario. Una vez que tenga el ID de proceso hijo, hay varias opciones para capturar volcados de hebras y volcados de almacenamiento dinámico.

Capturando volcados de hilos:

Hay 8 opciones para capturar volcados de subprocesos:

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

Los detalles sobre cada opción se pueden encontrar en este artículo . Una vez que haya capturado los volcados de subprocesos, puede usar herramientas como fastThread , Samuraito analiza los volcados de subprocesos.

Capturando volcados del montón:

Hay 7 opciones para capturar volcados de almacenamiento dinámico:

  1. jmap

  2. -XX: + HeapDumpOnOutOfMemoryError

  3. jcmd

  4. JVisualVM

  5. JMX

  6. Enfoque programático

  7. Consolas administrativas

Los detalles sobre cada opción se pueden encontrar en este artículo . Una vez que haya capturado el volcado de almacenamiento dinámico, puede usar herramientas como la herramienta de Análisis de memoria de Eclipse , HeapHero para analizar los volcados de almacenamiento dinámico capturados.


-1

En un Oracle JDK, tenemos un comando llamado jmap (disponible en la carpeta bin de Java Home). El uso del comando viene de la siguiente manera

jmap (opción) (pid)

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.