¿Qué es Thread Dump y cómo analizarlos?

Hablemos sobre el volcado de subprocesos y cómo analizarlo.

También discutiremos cómo ayuda a identificar los problemas y algunos de los analizadores que puede usar.

¿Qué es Hilo?

Un proceso es un programa de computadora que se carga en la memoria de la computadora y está en ejecución. Puede ser ejecutado por un procesador o un conjunto de procesadores. Un proceso se describe en la memoria con información importante, como almacenes de variables, identificadores de archivos, el contador del programa, registros y señales, etc.

Un proceso puede constar de muchos procesos ligeros llamados subprocesos. Esto ayuda a lograr el paralelismo en el que un proceso se divide en varios subprocesos. Esto da como resultado un mejor rendimiento. Todos los subprocesos dentro de un proceso comparten el mismo espacio de memoria y dependen unos de otros.

Volcados de subprocesos

Cuando el proceso se está ejecutando, podemos detectar el estado actual de ejecución de los subprocesos en el proceso mediante volcados de subprocesos. Un volcado de subprocesos contiene una instantánea de todos los subprocesos activos en un punto determinado durante la ejecución de un programa. Contiene toda la información relevante sobre el hilo y su estado actual.

Una aplicación moderna hoy en día involucra múltiples números de subprocesos. Cada hilo requiere ciertos recursos, realiza ciertas actividades relacionadas con el proceso. Esto puede aumentar el rendimiento de una aplicación, ya que los subprocesos pueden utilizar los núcleos de CPU disponibles.

Pero hay ventajas y desventajas, por ejemplo, a veces varios subprocesos pueden no coordinarse bien entre sí y puede surgir una situación de interbloqueo. Entonces, si algo sale mal, podemos usar volcados de subprocesos para inspeccionar el estado de nuestros subprocesos.

Volcado de hilo en Java

Un volcado de subprocesos de JVM es una lista del estado de todos los subprocesos que forman parte del proceso en ese momento determinado. Contiene información sobre la pila del subproceso, presentada como un seguimiento de la pila. Como está escrito en texto sin formato, los contenidos se pueden guardar para revisarlos más tarde. El análisis de volcados de subprocesos puede ayudar en

  • Optimización del rendimiento de JVM
  • Optimización del rendimiento de la aplicación
  • Diagnóstico de problemas, por ejemplo, interbloqueo, contención de subprocesos, etc.

Generación de volcados de subprocesos

Hay muchas formas de generar volcados de subprocesos. A continuación se muestran algunas herramientas basadas en JVM y se pueden ejecutar desde la línea de comandos/terminal (herramientas CLI) o el directorio /bin (herramientas GUI) de la carpeta de instalación de Java.

Explorémoslos.

#1. jStack

La forma más sencilla de generar un volcado de subprocesos es mediante jStack. jStack se envía con JVM y se puede usar desde la línea de comandos. Aquí, necesitamos el PID del proceso para el que queremos generar el volcado de subprocesos. Para obtener PID podemos usar el comando jps como se muestra a continuación.

jps-l

jps enumera todos los ID de proceso de Java.

en ventanas

C:Program FilesJavajdk1.8.0_171bin>jps -l
47172 portal
6120 sun.tools.jps.Jps
C:Program FilesJavajdk1.8.0_171bin>

en linux

[[email protected] ~]# jps -l
1088 /opt/keycloak/jboss-modules.jar
26680 /var/lib/jenkins/workspace/kyc/kyc/target/kyc-1.0.jar
7193 jdk.jcmd/sun.tools.jps.Jps
2058 /usr/share/jenkins/jenkins.war
11933 /var/lib/jenkins/workspace/admin-portal/target/portal-1.0.jar
[[email protected] ~]#

Como podemos ver aquí, obtenemos una lista de todos los procesos Java en ejecución. Contiene la identificación de la VM local para el proceso Java en ejecución y el nombre de la aplicación en las columnas uno y dos respectivamente. Ahora, para generar el volcado de subprocesos, usamos el programa jStack con el indicador –l que crea una salida larga del volcado. También podemos canalizar la salida a algún archivo de texto de nuestra elección.

jstack -l 26680

[[email protected] ~]# jstack -l 26680
2020-06-27 06:04:53
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):

"Attach Listener" #16287 daemon prio=9 os_prio=0 tid=0x00007f0814001800 nid=0x4ff2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"logback-8" #2316 daemon prio=5 os_prio=0 tid=0x00007f07e0033000 nid=0x4792 waiting on condition [0x00007f07baff8000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006ca9a1fc0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

"logback-7" #2315 daemon prio=5 os_prio=0 tid=0x00007f07e0251800 nid=0x4791 waiting on condition [0x00007f07bb0f9000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006ca9a1fc0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

   Locked ownable synchronizers:
        - None

#2. jvisualvm

Jvisualvm es una herramienta GUI que nos ayuda a solucionar problemas, monitorear y perfilar aplicaciones Java. También viene con JVM y se puede iniciar desde el directorio /bin de nuestra instalación de Java. Es muy intuitivo y fácil de usar. Entre otras opciones, también nos permite capturar el volcado de subprocesos para un proceso en particular.

Para ver el volcado de subprocesos de un proceso en particular, podemos hacer clic con el botón derecho en el programa y seleccionar Volcado de subprocesos en el menú contextual.

#3. jcmd

JCMD es una utilidad de línea de comandos que se envía con el JDK y se utiliza para enviar solicitudes de comandos de diagnóstico a la JVM.

Sin embargo, solo funciona en la máquina local donde se ejecuta la aplicación Java. Se puede utilizar para controlar las grabaciones de vuelo de Java, diagnosticar y solucionar problemas de aplicaciones JVM y Java. Podemos usar el comando Thread.print de jcmd para obtener una lista de volcados de subprocesos para un proceso particular especificado por el PID.

A continuación se muestra un ejemplo de cómo podemos usar jcmd.

jcmd 28036 Hilo.imprimir

C:Program FilesJavajdk1.8.0_171bin>jcmd 28036 Thread.print
28036:
2020-06-27 21:20:02
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.171-b11 mixed mode):

"Bundle File Closer" #14 daemon prio=5 os_prio=0 tid=0x0000000021d1c000 nid=0x1d4c in Object.wait() [0x00000000244ef000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Unknown Source)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.getNextEvent(EventManager.java:403)
        - locked <0x000000076f380a88> (a org.eclipse.osgi.framework.eventmgr.EventManager$EventThread)
        at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:339)

"Active Thread: Equinox Container: 0b6cc851-96cd-46de-a92b-253c7f7671b9" #12 prio=5 os_prio=0 tid=0x0000000022e61800 nid=0xbff4 waiting on condition [0x00000000243ee000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000076f388188> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(Unknown Source)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
        at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.getTask(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.lang.Thread.run(Unknown Source)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000021a7b000 nid=0x2184 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x00000000219f5000 nid=0x1300 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x00000000219e0000 nid=0x48f4 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x00000000219df000 nid=0xb314 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x00000000219db800 nid=0x2260 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000219d9000 nid=0x125c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000219d8000 nid=0x834 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001faf3000 nid=0x36c0 in Object.wait() [0x0000000021eae000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076f390180> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        - locked <0x000000076f390180> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000005806000 nid=0x13c0 in Object.wait() [0x00000000219af000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076f398178> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Unknown Source)
        at java.lang.ref.Reference.tryHandlePending(Unknown Source)
        - locked <0x000000076f398178> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

"main" #1 prio=5 os_prio=0 tid=0x000000000570e800 nid=0xbf8 runnable [0x0000000000fec000]
   java.lang.Thread.State: RUNNABLE
        at java.util.zip.ZipFile.open(Native Method)
        at java.util.zip.ZipFile.<init>(Unknown Source)
        at java.util.zip.ZipFile.<init>(Unknown Source)
        at java.util.zip.ZipFile.<init>(Unknown Source)
        at org.eclipse.osgi.framework.util.SecureAction.getZipFile(SecureAction.java:307)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.getZipFile(ZipBundleFile.java:136)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.lockOpen(ZipBundleFile.java:83)
        at org.eclipse.osgi.storage.bundlefile.ZipBundleFile.getEntry(ZipBundleFile.java:290)
        at org.eclipse.equinox.weaving.hooks.WeavingBundleFile.getEntry(WeavingBundleFile.java:65)
        at org.eclipse.osgi.storage.bundlefile.BundleFileWrapper.getEntry(BundleFileWrapper.java:55)
        at org.eclipse.osgi.storage.BundleInfo$Generation.getRawHeaders(BundleInfo.java:130)
        - locked <0x000000076f85e348> (a java.lang.Object)
        at org.eclipse.osgi.storage.BundleInfo$CachedManifest.get(BundleInfo.java:599)
        at org.eclipse.osgi.storage.BundleInfo$CachedManifest.get(BundleInfo.java:1)
        at org.eclipse.equinox.weaving.hooks.SupplementerRegistry.addSupplementer(SupplementerRegistry.java:172)
        at org.eclipse.equinox.weaving.hooks.WeavingHook.initialize(WeavingHook.java:138)
        at org.eclipse.equinox.weaving.hooks.WeavingHook.start(WeavingHook.java:208)
        at org.eclipse.osgi.storage.FrameworkExtensionInstaller.startActivator(FrameworkExtensionInstaller.java:261)
        at org.eclipse.osgi.storage.FrameworkExtensionInstaller.startExtensionActivators(FrameworkExtensionInstaller.java:198)
        at org.eclipse.osgi.internal.framework.SystemBundleActivator.start(SystemBundleActivator.java:112)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:815)
        at org.eclipse.osgi.internal.framework.BundleContextImpl$3.run(BundleContextImpl.java:1)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.startActivator(BundleContextImpl.java:808)
        at org.eclipse.osgi.internal.framework.BundleContextImpl.start(BundleContextImpl.java:765)
        at org.eclipse.osgi.internal.framework.EquinoxBundle.startWorker0(EquinoxBundle.java:1005)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle$EquinoxSystemModule.initWorker(EquinoxBundle.java:190)
        at org.eclipse.osgi.container.SystemModule.init(SystemModule.java:99)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle.init(EquinoxBundle.java:272)
        at org.eclipse.osgi.internal.framework.EquinoxBundle$SystemBundle.init(EquinoxBundle.java:257)
        at org.eclipse.osgi.launch.Equinox.init(Equinox.java:171)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.startup(EclipseStarter.java:316)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:251)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:661)
        at org.eclipse.equinox.launcher.Main.basicRun(Main.java:597)
        at org.eclipse.equinox.launcher.Main.run(Main.java:1476)

"VM Thread" os_prio=2 tid=0x000000001fae8800 nid=0x32cc runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000005727800 nid=0x3264 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000005729000 nid=0xbdf4 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000572a800 nid=0xae6c runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000572d000 nid=0x588 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000000000572f000 nid=0xac0 runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000005730800 nid=0x380 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000005733800 nid=0x216c runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000005734800 nid=0xb930 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000000021a8d000 nid=0x2dcc waiting on condition

JNI global references: 14


C:Program FilesJavajdk1.8.0_171bin>

#4. JMC

JMC significa Java Mission Control. Es una herramienta GUI de código abierto que se envía con JDK y se utiliza para recopilar y analizar datos de aplicaciones Java.

Se puede iniciar desde la carpeta /bin de nuestra instalación de Java. Los administradores y desarrolladores de Java utilizan la herramienta para recopilar información detallada de bajo nivel sobre los comportamientos de la aplicación y la JVM. Permite un análisis detallado y eficiente de los datos recopilados por Java Flight Recorder.

Al iniciar jmc, podemos ver la lista de procesos Java que se ejecutan en la máquina local. También es posible una conexión remota. En un proceso en particular, podemos hacer clic derecho y elegir Iniciar grabación de vuelo y luego verificar los volcados de subprocesos en la pestaña Subprocesos.

#5. jconsole

jconsole es una herramienta de extensión de gestión de Java que se utiliza para la gestión y el control de quejas.

También tiene un conjunto de operaciones predefinidas en el agente JMX que el usuario puede realizar. Permite al usuario detectar y analizar el seguimiento de la pila de un programa en vivo. Se puede iniciar desde la carpeta /bin de nuestra instalación de Java.

Usando la herramienta jconsole GUI, podemos inspeccionar el seguimiento de la pila de cada subproceso cuando lo conectamos a un proceso Java en ejecución. Luego, en la pestaña Subproceso, podemos ver el nombre de todos los subprocesos en ejecución. Para detectar un interbloqueo, podemos hacer clic en Detectar interbloqueo en la parte inferior derecha de la ventana. Si se detecta un interbloqueo, aparecerá en una nueva pestaña; de lo contrario, se mostrará No se detectaron interbloqueos.

#6. SubprocesoMxBean

ThreadMXBean es la interfaz para la gestión del sistema de hilos de la máquina virtual Java perteneciente al paquete java.lang.Management. Se utiliza principalmente para detectar los subprocesos que han entrado en una situación de interbloqueo y obtener detalles sobre ellos.

Podemos usar la interfaz ThreadMxBean para capturar mediante programación el volcado de subprocesos. El método getThreadMXBean() de ManagementFactory se usa para obtener una instancia de la interfaz ThreadMXBean. Devuelve el número de subprocesos activos tanto daemon como no daemon. ManagementFactory es una clase de fábrica para obtener beans administrados para la plataforma Java.

private static String getThreadDump (boolean lockMonitors, boolean lockSynchronizers) {
    StringBuffer threadDump = new StringBuffer (System.lineSeparator ());
    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean ();
    for (ThreadInfo threadInfo : threadMXBean.dumpAllThreads (lockMonitors, lockSynchronizers)) {
        threadDump.append (threadInfo.toString ());
    }
    return threadDump.toString ();
}

Análisis manual de volcados de subprocesos

El análisis de volcados de subprocesos puede ser muy útil para identificar problemas en procesos de subprocesos múltiples. Los problemas como los interbloqueos, la contención de bloqueos y el uso excesivo de la CPU por volcados de subprocesos individuales se pueden resolver visualizando los estados de los volcados de subprocesos individuales.

El rendimiento máximo de la aplicación se puede lograr rectificando el estado de cada subproceso después de analizar el volcado del subproceso.

Por ejemplo, digamos que un proceso está utilizando una gran cantidad de CPU, podemos averiguar si algún subproceso está utilizando más la CPU. Si existe tal subproceso, convertimos su número LWP a un número hexadecimal. Luego, desde el volcado de subprocesos, podemos encontrar el subproceso con nid igual al número hexadecimal obtenido anteriormente. Usando el seguimiento de la pila del hilo, podemos identificar el problema. Averigüemos la identificación del proceso del hilo usando el siguiente comando.

ps -mo pid,lwp,stime,time,cpu -C java

[[email protected] ~]# ps -mo pid,lwp,stime,time,cpu -C java
       PID        LWP         STIME           TIME              %CPU
26680               -         Dec07          00:02:02           99.5
         -       10039        Dec07          00:00:00           0.1
         -       10040        Dec07          00:00:00           95.5

Echemos un vistazo a la parte inferior del volcado de subprocesos. Para obtener el volcado de subprocesos para el proceso 26680, use jstack -l 26680

[[email protected] ~]# jstack -l 26680
2020-06-27 09:01:29
<strong>Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode):</strong>

"Attach Listener" #16287 daemon prio=9 os_prio=0 tid=0x00007f0814001800 nid=0x4ff2 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

.
.
.
.
.
.
.
"<strong>Reference Handler</strong>" #2 daemon prio=10 os_prio=0 tid=0x00007f085814a000 nid=0x6840 in Object.wait() [0x00007f083b2f1000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000006c790fbd0> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

   Locked ownable synchronizers:
        - None

"VM Thread" os_prio=0 tid=0x00007f0858140800 nid=0x683f runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f0858021000 nid=0x683b runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f0858022800 nid=0x683c runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f0858024800 nid=0x683d runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f0858026000 nid=0x683e runnable

"VM Periodic Task Thread" os_prio=0 tid=0x00007f08581a0000 nid=0x6847 waiting on condition

JNI global references: 1553

Ahora, veamos cuáles son las cosas que podemos explorar usando volcados de subprocesos. Si observamos el volcado del hilo, podemos ver una gran cantidad de contenido, que puede ser abrumador. Sin embargo, si damos un paso a la vez, puede ser bastante simple de entender. Entendamos la primera línea.

2020-06-27 09:01:29
Volcado completo de subprocesos Java HotSpot(TM) Servidor virtual de 64 bits (modo mixto 25.221-b11):

Lo anterior muestra la hora en que se generó el volcado e información sobre la JVM que se utilizó. A continuación, al final, podemos ver la lista de hilos, el primero de ellos es nuestro hilo ReferenceHandler.

Análisis de subprocesos bloqueados

Si analizamos los registros de volcado de subprocesos a continuación, podemos encontrar que ha detectado subprocesos con estado BLOQUEADO, lo que hace que el rendimiento de una aplicación sea muy lento. Entonces, si podemos encontrar los subprocesos BLOQUEADOS, podemos intentar extraer los subprocesos relacionados con los bloqueos que los subprocesos están tratando de obtener. El análisis del seguimiento de la pila del subproceso que actualmente contiene el bloqueo puede ayudar a resolver el problema.

[[email protected] ~]# jstack -l 26680
.
.
.
.
" DB-Processor-13" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f000]
java.lang.Thread.State: <strong>BLOCKED</strong> (on object monitor)
                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
                - waiting to lock <0xe0375410> (a beans.ConnectionPool)
                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)
                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)
"DB-Processor-14" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f020]
java.lang.Thread.State: <strong>BLOCKED</strong> (on object monitor)
                at beans.ConnectionPool.getConnection(ConnectionPool.java:102)
                - waiting to lock <0xe0375410> (a beans.ConnectionPool)
                at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111)
                at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43)
.
.
.
.

Análisis de subprocesos interbloqueados

Otra aplicación muy utilizada de volcados de subprocesos es la detección de interbloqueos. La detección y solución de interbloqueos puede ser mucho más fácil si analizamos los volcados de subprocesos.

Un interbloqueo es una situación que involucra al menos dos subprocesos donde el recurso requerido por un subproceso para continuar la ejecución está bloqueado por otro subproceso y, al mismo tiempo, el recurso requerido por el segundo subproceso está bloqueado por el primer subproceso.

Por lo tanto, ninguno de los subprocesos puede continuar con la ejecución, y esto da como resultado una situación de interbloqueo y termina con la aplicación bloqueada. Si hay rastas, la sección final del volcado de subprocesos imprimirá la información sobre el interbloqueo de la siguiente manera.

"Thread-0":
waiting to lock monitor 0x00000250e4982480 (object 0x00000000894465b0, a java.lang.Object),
which is held by "Thread-1"
"Thread-1":
waiting to lock monitor 0x00000250e4982380 (object 0x00000000894465a0, a java.lang.Object),
which is held by "Thread-0"
.
.
.
"Thread-0":
at DeadlockedProgram$DeadlockedRunnableImplementation.run(DeadlockedProgram.java:34)
- waiting to lock <0x00000000894465b0> (a java.lang.Object)
- locked <0x00000000894465a0> (a java.lang.Object)
at java.lang.Thread.run([email protected]/Thread.java:844)
"Thread-1":
at DeadlockedProgram $DeadlockRunnableImplementation.run(DeadlockedProgram.java:34)
- waiting to lock <0x00000000894465a0> (a java.lang.Object)
- locked <0x00000000894465b0> (a java.lang.Object)
at java.lang.Thread.run([email protected]/Thread.java:844)

Aquí podemos ver la información de punto muerto en un formato bastante legible por humanos.

Aparte de esto, si sumamos todos los fragmentos de volcado de subprocesos anteriores, entonces indica la siguiente información.

  • El controlador de referencia es el nombre legible por humanos del subproceso.
  • #2 es la identificación única del hilo.
  • daemon indica si el subproceso es un subproceso daemon.
  • La prioridad numérica del subproceso viene dada por prio=10.
  • El estado actual del subproceso se indica con la condición de espera.
  • Luego vemos el seguimiento de la pila, que incluye la información de bloqueo.

Analizadores de volcados de subprocesos

Además del análisis manual, existen numerosas herramientas disponibles para analizar volcados de subprocesos, tanto en línea como fuera de línea. A continuación se muestran algunas de las herramientas enumeradas, que podemos utilizar en función de los requisitos.

Primero, exploremos las herramientas en línea.

#1. Hilo rápido

Hilo rápido es la herramienta de análisis de volcado de subprocesos favorita de los ingenieros de DevOps para solucionar problemas de producción complejos. Este es un analizador de volcado de subprocesos de Java en línea. Podemos cargar el volcado de subprocesos como un archivo o podemos copiar y pegar directamente el volcado de subprocesos.

Dependiendo del tamaño, analizará el volcado de subprocesos y mostrará la información como se muestra en la captura de pantalla.

Características

  • Solucionar fallas de JVM, ralentizaciones, fugas de memoria, bloqueos, picos de CPU
  • RCA instantáneo (no espere a los proveedores)
  • Tablero intuitivo
  • Compatibilidad con API REST
  • Aprendizaje automático

#2. Analizador de volcado de subprocesos de Spotify

los Analizador de volcado de subprocesos de Spotify tiene licencia bajo la versión 2.0 de la licencia Apache. Es una herramienta en línea y acepta el volcado de subprocesos como un archivo o podemos copiar y pegar directamente el volcado de subprocesos. Dependiendo del tamaño, analizará el volcado de subprocesos y mostrará la información como se muestra en la captura de pantalla.

#3. revisión jstack

Jstack.revisión analiza los volcados de subprocesos de Java desde el navegador. Esta página es solo del lado del cliente.

#4. Sitio 24×7

Este herramienta es un requisito previo para detectar subprocesos defectuosos que degradan el rendimiento de Java Virtual Machine (JVM). Los problemas como los interbloqueos, la contención de bloqueos y el uso excesivo de la CPU por volcados de subprocesos individuales se pueden resolver visualizando los estados de los volcados de subprocesos individuales.

Se puede lograr el máximo rendimiento de la aplicación rectificando el estado de cada subproceso proporcionado por la herramienta.

Ahora, exploremos las herramientas fuera de línea.

Cuando se trata de crear perfiles, solo la mejor herramienta es lo suficientemente buena.

#1. JProfiler

JProfiler es uno de los analizadores de volcado de subprocesos más populares entre los desarrolladores de Java. La interfaz de usuario intuitiva de JProfiler lo ayuda a resolver los cuellos de botella en el rendimiento, detectar fugas de memoria y comprender los problemas de subprocesos.

JProfiler admite la creación de perfiles en las siguientes plataformas:

  • ventanas
  • Mac OS
  • linux
  • FreeBSD
  • Solaris
  • AIX
  • HP-UX

A continuación se presentan algunas características que hacen de JProfiler la mejor opción para perfilar nuestras aplicaciones en la JVM.

Características

  • Admite la creación de perfiles de bases de datos para JDBC, JPA y NoSQL
  • El soporte para la edición empresarial de Java también está disponible
  • Presenta información de alto nivel sobre llamadas RMI
  • Análisis estelar de fugas de memoria
  • Amplias capacidades de control de calidad
  • El generador de perfiles de subprocesos integrado está estrechamente integrado con las vistas de generación de perfiles de CPU.
  • Soporte para plataformas, IDE’s y servidores de aplicaciones.

#2. IBM TMDA

IBM Thread and Monitor Dump Analyzer para Java (TMDA) es una herramienta que permite la identificación de bloqueos, interbloqueos, contención de recursos y cuellos de botella en volcados de subprocesos de Java. Es un producto de IBM, pero la herramienta TMDA se proporciona sin garantía ni soporte; sin embargo, intentan arreglar y mejorar la herramienta con el tiempo.

#3. Administrar motor

Administrar motor El administrador de aplicaciones puede ayudar a monitorear la memoria JVM Heap y Non-Heap. Incluso podemos configurar umbrales y recibir alertas por correo electrónico, SMS, etc., y garantizar que una aplicación Java esté bien ajustada.

#4. TuKit

TuKit consta de los siguientes productos denominados Kit.

  • Java Profiler: generador de perfiles de baja sobrecarga con todas las funciones para las plataformas Java EE y Java SE.
  • YouMonitor: supervisión del rendimiento y creación de perfiles de Jenkins, TeamCity, Gradle, Maven, Ant, JUnit y TestNG.
  • .NET Profiler: perfilador de rendimiento y memoria fácil de usar para .NET framework.

Conclusión

Ahora ya sabe cómo los volcados de subprocesos son útiles para comprender y diagnosticar problemas en aplicaciones multiproceso. con la adecuada conocimientocon respecto a los volcados de subprocesos (su estructura, la información contenida en ellos, etc.), podemos utilizarlos para identificar las causas de los problemas rápidamente.