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

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

En Unix, podría hacer un kill -3 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 botín del proceso principal, no del niño.

¿Hay alguna otra forma de obtener ese basurero?

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

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

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

para obtener el montón para ese proceso.

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

Thread dump = stack traces para cada thread en la salida de JVM a stdout como texto.

Heap dump = contenido de la memoria para la salida del proceso JVM a un archivo binario.

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

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

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

Solo para terminar la pregunta de volcado: los volcados de stack no se usan comúnmente porque son difíciles de interpretar. Pero, tienen mucha información útil en ellos si sabes dónde / cómo mirarlos. El uso más común es localizar memory leaks. Es una buena práctica establecer el -D en la línea de comando java para que el volcado del montón se genere automáticamente en un OutOfMemoryError, -XX:+HeapDumpOnOutOfMemoryError Pero también puede desencadenar 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, jmap está disponible en Windows.

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

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

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

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

Además de usar la jconsole / visualvm mencionada, puede usar jstack -l en otra ventana de línea de comando y capturar esa salida.

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

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

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

Pruebe una de las siguientes opciones.

  1. Para JVM de 32 bits:

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

     jmap -J-d64 -dump:format=b,file=  
  3. Para la JVM de 64 bits con el algoritmo G1GC en los parámetros de VM (solo el heap de objetos en vivo se genera con el algoritmo G1GC):

     jmap -J-d64 -dump:live,format=b,file=  

Pregunta de SE relacionada: Error de volcado de la stack Java con el comando jmap: EOF prematuro

Eche un vistazo a varias opciones de jmap en este artículo

Si quiere un heapdump en memoria insuficiente, puede iniciar Java con la opción -XX:-HeapDumpOnOutOfMemoryError

cf página de referencia de opciones de JVM

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

 jcmd PID GC.heap_dump /tmp/dump 

Puede ejecutar jconsole (incluido con el SDK de Java 6) y luego conectarse a su aplicación Java. Le mostrará cada hilo que se ejecuta y su rastro de stack.

Puede enviar kill -3 desde Cygwin. Tienes que usar las opciones de Cygwin ps para encontrar los procesos de Windows y luego enviar la señal a ese proceso.

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

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

Si está en Windows Machine, puede usar el Administrador de tareas para obtener PID. Para la máquina 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 muchas herramientas disponibles para interpretar el archivo hprof. Recomendaré la herramienta visualvm de Oracle, que es simple de usar.

Escribí un pequeño script por lotes para Java 8 (usando PsExec y jcmd ) llamado jvmdump.bat , que jvmdump.bat los hilos, 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) que 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 .

Si no puede (o no quiere) utilizar la consola / terminal por algún motivo, existe una solución alternativa. Puede hacer que la aplicación Java imprima el volcado de hilo por usted. El código que recostack el Stack Trace es razonablemente simple y se puede adjuntar a un botón o una interfaz web.

 private static String getThreadDump() { Map allStackTraces = Thread.getAllStackTraces(); StringBuilder out = new StringBuilder(); for (Map.Entry 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.(PlainSocketImpl.java:45) java.net.Socket.setImpl(Socket.java:503) java.net.Socket.(Socket.java:424) java.net.Socket.(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 transmisiones, el código es aún más compacto:

 private static String getThreadDump() { Map 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(); } 

Puedes probar fácilmente este código con:

 System.out.print(getThreadDump()); 

En 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)