java jvm heap-dump thread-dump

java - heap dump analyzer



Cómo obtener un hilo y un volcado de pila de un proceso Java en Windows que no se está ejecutando en una consola (15)

Además de usar el jconsole / visualvm mencionado, puede usar 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 el identificador del proceso en Windows y Unix), o usando jps .

Tanto jstack como jps se incluyen en Sun JDK versión 6 y superior.

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

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

¿Hay otra manera de conseguir ese montón de basura?


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}


En un JDK de Oracle, tenemos un comando llamado jmap (disponible en la carpeta bin de Java Home). el uso del comando viene como sigue

jmap (opción) (pid)

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


Escribí un pequeño script por lotes para Java 8 (usando PsExec y jcmd ) llamado jvmdump.bat , que descarga los subprocesos, el montón, las propiedades del sistema y los argumentos de 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 en la Session 0 y 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) para View the message en la sesión interactiva, que lo llevará a la nueva consola en la otra sesión desde la que puede ejecutar el script jvmdump.bat .


Estás confundiendo dos volcados de Java diferentes. kill -3 genera un volcado de hilo, no un volcado de pila.

Volcado de hilo = seguimientos de la pila para cada hilo en la salida JVM a la salida estándar como texto.

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

Para realizar un volcado de hilos en Windows, CTRL + BREAK si su JVM es el proceso de primer plano es la forma más sencilla. Si tiene un shell de tipo Unix en Windows como Cygwin o MobaXterm, puede usar kill -3 {pid} como en Unix.

Para realizar un volcado de hilos en Unix, CTRL + C si su JVM es el proceso de 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 de descarte: los volcados de pila no se usan comúnmente porque son difíciles de interpretar. Pero tienen mucha información útil si saben dónde y cómo mirarlos. El uso más común es localizar las fugas de memoria. Es una buena práctica establecer la -D en la línea de comandos de java para que el volcado de pila se genere automáticamente en un OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError Pero también puede activar manualmente un volcado de pila. La forma más común es usar la utilidad jmap .

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

Un ejemplo de línea de comandos se vería algo así como

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

La salida "myheap.bin" no es legible para el ser humano (para la mayoría de nosotros), y necesitará una herramienta para analizarla. Mi preferencia es MAT. http://www.eclipse.org/mat/


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 de 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 de SE relacionada: Error de volcado de pila de Java con el comando jmap: EOF prematura

Echa un vistazo a varias opciones de jmap en este article


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


Puede usar jmap para obtener un volcado de cualquier proceso en ejecución, asumiendo que conoce el pid .

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

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

para obtener el montón para ese proceso.


Puedes enviar el kill -3 <pid> desde Cygwin. Tiene que usar las opciones de Cygwin ps para encontrar procesos de Windows y luego enviar la señal a ese proceso.


Recomiendo Java VisualVM distribuido con el JDK (jvisualvm.exe). Puede conectarse dinámicamente y acceder a los hilos y el montón. He encontrado en invaluable para algunos problemas.



Si está utilizando JDK 1.6 o superior, puede usar el comando jmap para tomar un volcado de pila de un proceso de Java, condición que debe conocer ProcessID.

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

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

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


Si estás en server-jre 8 y arriba puedes usar esto:

jcmd PID GC.heap_dump /tmp/dump


Si no puede (o no quiere) usar la consola / terminal por alguna razón, existe una solución alternativa. Puede hacer que la aplicación Java imprima el volcado de hilos por usted. El código que recopila el seguimiento de la pila es bastante 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 de Java 8 con secuencias, 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());


Tienes que redirigir la salida del segundo ejecutable java a algún archivo. Luego, use SendSignal para enviar "-3" a su segundo proceso.