O que é Thread Dump e como analisá-los?

Vamos falar sobre o despejo de thread e como analisá-lo.

Também discutiremos como isso ajuda a identificar os problemas e alguns dos analisadores que você pode usar.

O que é Thread?

Um processo é um programa de computador que é carregado na memória do computador e está em execução. Pode ser executado por um processador ou por um conjunto de processadores. Um processo é descrito na memória com informações importantes, como armazenamento de variáveis, manipuladores de arquivos, contador de programa, registradores e sinais, e assim por diante.

Um processo pode consistir em muitos processos leves chamados threads. Isso ajuda a alcançar o paralelismo em que um processo é dividido em vários threads. Isso resulta em melhor desempenho. Todos os threads dentro de um processo compartilham o mesmo espaço de memória e são dependentes uns dos outros.

Despejos de discussão

Quando o processo está em execução, podemos detectar o estado atual de execução dos threads no processo usando dumps de thread. Um dump de thread contém um instantâneo de todos os threads ativos em um determinado ponto durante a execução de um programa. Ele contém todas as informações relevantes sobre o thread e seu estado atual.

Um aplicativo moderno hoje envolve vários números de threads. Cada thread requer determinados recursos, executa determinadas atividades relacionadas ao processo. Isso pode aumentar o desempenho de um aplicativo, pois os threads podem utilizar os núcleos de CPU disponíveis.

Mas há compensações, por exemplo, às vezes vários threads podem não se coordenar bem entre si e pode surgir uma situação de impasse. Portanto, se algo der errado, podemos usar dumps de thread para inspecionar o estado de nossos threads.

Despejo de thread em Java

Um despejo de encadeamento JVM é uma listagem do estado de todos os encadeamentos que fazem parte do processo naquele ponto específico do tempo. Ele contém informações sobre a pilha do thread, apresentadas como um rastreamento de pilha. Como está escrito em texto simples, o conteúdo pode ser salvo para revisão posterior. A análise de despejos de encadeamento pode ajudar em

  • Otimizando o desempenho da JVM
  • Otimizando o desempenho do aplicativo
  • Diagnosticar problemas, por exemplo, um impasse, contenção de encadeamento, etc.

Geração de Thread Dumps

Há muitas maneiras de gerar despejos de encadeamento. Abaixo estão algumas ferramentas baseadas em JVM e podem ser executadas a partir da linha de comando/terminal (ferramentas CLI) ou no diretório /bin (ferramentas GUI) da pasta de instalação do Java.

Vamos explorá-los.

#1. jStack

A maneira mais simples de gerar um despejo de thread é usando jStack. jStack vem com JVM e pode ser usado na linha de comando. Aqui, precisamos do PID do processo para o qual queremos gerar o dump da thread. Para obter o PID, podemos usar o comando jps conforme mostrado abaixo.

jps -l

jps lista todos os IDs de processo java.

No Windows

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

No 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 aqui, obtemos uma lista de todos os processos java em execução. Ele contém o ID da VM local para o processo java em execução e o nome do aplicativo nas colunas um e dois, respectivamente. Agora, para gerar o despejo de encadeamento, usamos o programa jStack com o sinalizador –l que cria uma saída longa listada do despejo. Também podemos canalizar a saída para algum arquivo de texto de nossa escolha.

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 é uma ferramenta GUI que nos ajuda a solucionar problemas, monitorar e criar perfis de aplicativos Java. Ele também vem com JVM e pode ser iniciado a partir do diretório /bin de nossa instalação java. É muito intuitivo e fácil de usar. Entre outras opções, também permite capturar o dump do thread de um determinado processo.

  Como destacar texto em sua apresentação do PowerPoint

Para visualizar o despejo de encadeamento de um processo específico, podemos clicar com o botão direito do mouse no programa e selecionar Despejo de encadeamento no menu de contexto.

#3. jcmd

JCMD é um utilitário de linha de comando que acompanha o JDK e é usado para enviar solicitações de comando de diagnóstico para a JVM.

No entanto, funciona apenas na máquina local onde o aplicativo Java está sendo executado. Ele pode ser usado para controlar gravações de voo Java, diagnosticar e solucionar problemas de aplicativos JVM e Java. Podemos usar o comando Thread.print do jcmd para obter uma lista de despejos de encadeamento para um processo específico especificado pelo PID.

Abaixo está um exemplo de como podemos usar o jcmd.

jcmd 28036 Thread.print

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. É uma ferramenta GUI de código aberto que acompanha o JDK e é usada para coletar e analisar dados de aplicativos java.

Ele pode ser iniciado a partir da pasta /bin da nossa instalação Java. Os administradores e desenvolvedores Java usam a ferramenta para reunir informações detalhadas de baixo nível sobre os comportamentos da JVM e do aplicativo. Ele permite uma análise detalhada e eficiente dos dados coletados pelo Java Flight Recorder.

Ao iniciar o jmc, podemos ver a lista de processos java que estão sendo executados na máquina local. Uma conexão remota também é possível. Em um processo específico, podemos clicar com o botão direito do mouse e escolher Iniciar gravação de voo e, em seguida, verificar os despejos de thread na guia Threads.

#5. jconsole

jconsole é uma ferramenta Java Management Extension usada para gerenciamento e monitoramento de reclamações.

Ele também possui um conjunto de operações predefinidas no agente JMX que o usuário pode executar. Ele permite que o usuário detecte e analise o rastreamento de pilha de um programa ao vivo. Ele pode ser iniciado a partir da pasta /bin da nossa instalação Java.

Usando a ferramenta GUI jconsole, podemos inspecionar o rastreamento de pilha de cada encadeamento quando o conectamos a um processo java em execução. Em seguida, na guia Thread, podemos ver o nome de todos os threads em execução. Para detectar um impasse, podemos clicar em Detectar impasse no canto inferior direito da janela. Se um impasse for detectado, ele aparecerá em uma nova guia, caso contrário, nenhum impasse detectado será exibido.

  Como funcionam os testes de velocidade da Internet? (e quão precisos eles são?)

#6. ThreadMxBean

ThreadMXBean é a interface para o gerenciamento do sistema de threads da máquina virtual Java pertencente ao pacote java.lang.Management. É usado principalmente para detectar os encadeamentos que entraram em uma situação de impasse e obter detalhes sobre eles.

Podemos usar a interface ThreadMxBean para capturar programaticamente o despejo de encadeamento. O método getThreadMXBean() de ManagementFactory é usado para obter uma instância da interface ThreadMXBean. Ele retorna o número de encadeamentos ativos de daemon e não daemon. ManagementFactory é uma classe de fábrica para obter os beans gerenciados para a 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álise Manual de Thread Dumps

A análise de despejos de encadeamento pode ser muito útil para identificar problemas em processos multiencadeados. Problemas como bloqueios, contenção de bloqueio e utilização excessiva de CPU por despejos de encadeamento individuais podem ser resolvidos visualizando os estados de despejos de encadeamento individuais.

A taxa de transferência máxima do aplicativo pode ser alcançada retificando o status de cada encadeamento após a análise do despejo de encadeamento.

Por exemplo, digamos que um processo esteja consumindo muita CPU, podemos descobrir se algum thread está usando mais a CPU. Se houver tal thread, convertemos seu número LWP em um número hexadecimal. Então, a partir do despejo de thread, podemos encontrar o thread com nid igual ao número hexadecimal obtido anteriormente. Usando o rastreamento de pilha do thread, podemos identificar o problema. Vamos descobrir o ID do processo do thread usando o comando abaixo.

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

Vamos dar uma olhada no trecho abaixo do despejo de thread. Para obter o despejo de encadeamento para o processo 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

Agora, vamos ver quais são as coisas que podemos explorar usando dumps de thread. Se observarmos o despejo do encadeamento, veremos muito conteúdo, o que pode ser opressor. No entanto, se dermos um passo de cada vez, pode ser bastante simples de entender. Vamos entender a primeira linha

2020-06-27 09:01:29
Despejo de encadeamento completo Java HotSpot(TM) 64-Bit Server VM (modo misto 25.221-b11):

O exemplo acima exibe a hora em que o dump foi gerado e as informações sobre a JVM que foi usada. A seguir, no final, podemos ver a lista de threads, a primeira delas é a nossa thread ReferenceHandler.

Analisando Tópicos Bloqueados

Se analisarmos os logs de despejo de thread abaixo, podemos descobrir que ele detectou threads com status BLOQUEADO, o que torna o desempenho de um aplicativo muito lento. Então, se conseguirmos encontrar as threads BLOQUEADAS, podemos tentar extrair as threads relacionadas aos bloqueios que as threads estão tentando obter. A análise do rastreamento de pilha do encadeamento atualmente segurando o bloqueio pode ajudar a resolver o 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)
.
.
.
.

Analisando Thread em Deadlock

Outra aplicação muito usada de dumps de thread é a detecção de deadlocks. A detecção e solução de deadlocks pode ser muito mais fácil se analisarmos os thread dumps.

Um impasse é uma situação envolvendo pelo menos duas threads em que o recurso requerido por uma thread para continuar a execução é bloqueado por outra thread e, ao mesmo tempo, o recurso requerido pela segunda thread é bloqueado pela primeira thread.

Portanto, nenhum dos threads pode continuar a execução, e isso resulta em uma situação de impasse e faz com que o aplicativo fique preso. Se dreadlocks estiverem presentes, a seção final do despejo de encadeamento imprimirá as informações sobre o impasse da seguinte maneira.

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

Aqui podemos ver as informações de impasse em um formato bastante legível por humanos.

  Como consertar um Mac lento ou sem resposta

Fora isso, se somarmos todo o pedaço de despejo de encadeamento acima, ele indicará as informações abaixo.

  • O manipulador de referência é o nome legível do encadeamento.
  • #2 é o id exclusivo do thread.
  • daemon denota se o encadeamento é um encadeamento daemon.
  • A prioridade numérica do thread é dada por prio=10.
  • O status atual do encadeamento é indicado pela condição de espera.
  • Em seguida, vemos o rastreamento de pilha, que inclui as informações de bloqueio.

Analisadores de Thread Dumps

Além da análise manual, existem várias ferramentas disponíveis para analisar despejos de encadeamento, tanto online quanto offline. Abaixo estão algumas das ferramentas listadas, que podemos usar com base nos requisitos.

Primeiro, vamos explorar as ferramentas online.

#1. Tópico rápido

Tópico Rápido é a ferramenta de análise de despejo de encadeamento favorita do engenheiro de DevOps para solucionar problemas complexos de produção. Este é um analisador de despejo de encadeamento Java on-line. Podemos carregar o despejo de encadeamento como um arquivo ou podemos copiar e colar diretamente o despejo de encadeamento.

Dependendo do tamanho, ele analisará o despejo de thread e exibirá as informações conforme mostrado na captura de tela.

Características

  • Solucionar problemas de travamentos, lentidão, vazamentos de memória, congelamentos, picos de CPU da JVM
  • RCA instantâneo (não espere pelos fornecedores)
  • Painel intuitivo
  • Suporte à API REST
  • Aprendizado de máquina

#2. Spotify Thread Dump Analyzer

o Spotify Thread Dump Analyzer está licenciado sob a versão 2.0 da licença Apache. É uma ferramenta online e aceita o dump do thread como um arquivo ou podemos copiar e colar diretamente o dump do thread. Dependendo do tamanho, ele analisará o despejo de thread e exibirá as informações conforme mostrado na captura de tela.

#3. Revisão do Jstack

Jstack.review analisa despejos de encadeamento java de dentro do navegador. Esta página é apenas do lado do cliente.

#4. Local 24×7

este ferramenta é um pré-requisito para detectar encadeamentos defeituosos que degradam o desempenho da Java Virtual Machine (JVM). Problemas como bloqueios, contenção de bloqueio e utilização excessiva de CPU por despejos de encadeamento individuais podem ser resolvidos visualizando os estados de despejos de encadeamento individuais.

A taxa de transferência máxima do aplicativo pode ser alcançada retificando o status de cada encadeamento fornecido pela ferramenta.

Agora, vamos explorar as ferramentas off-line.

Quando se trata de criação de perfil, apenas a melhor ferramenta é boa o suficiente.

#1. JProfilerGenericName

JProfilerGenericName é um dos analisadores de despejo de encadeamento mais populares entre os desenvolvedores Java. A interface do usuário intuitiva do JProfiler ajuda a resolver gargalos de desempenho, identificar vazamentos de memória e entender problemas de encadeamento.

O JProfiler oferece suporte à criação de perfil nas seguintes plataformas:

  • janelas
  • Mac OS
  • Linux
  • FreeBSDGenericName
  • Solaris
  • AIX
  • HP-UX

Abaixo estão alguns recursos que tornam o JProfiler a melhor escolha para criação de perfil de nossos aplicativos na JVM.

Características

  • Suporta criação de perfil de banco de dados para JDBC, JPA e NoSQL
  • Suporte para Java Enterprise Edition também está disponível
  • Apresenta informações de alto nível sobre chamadas RMI
  • Análise estelar de vazamentos de memória
  • Extensos recursos de controle de qualidade
  • O criador de perfil de encadeamento integrado está totalmente integrado às exibições de criação de perfil da CPU.
  • Suporte para plataformas, IDEs e servidores de aplicativos.

#2. IBM TMDA

IBM Thread and Monitor Dump Analyzer para Java (TMDA) é uma ferramenta que permite a identificação de interrupções, conflitos, contenção de recursos e gargalos em dumps de encadeamento Java. É um produto IBM, mas a ferramenta TMDA é fornecida sem qualquer garantia ou suporte; no entanto, eles tentam corrigir e aprimorar a ferramenta ao longo do tempo.

#3. ManageEngine

ManageEngine o gerenciador de aplicativos pode ajudar a monitorar a memória JVM Heap e Non-Heap. Podemos até configurar limites e ser alertados por e-mail, SMS, etc, e garantir que um aplicativo Java esteja bem ajustado.

#4. SeuKit

SeuKit consiste nos produtos abaixo chamados de Kit.

  • Java Profiler – Profiler de baixa sobrecarga com todos os recursos para as plataformas Java EE e Java SE.
  • YouMonitor – Monitoramento de desempenho e criação de perfil de Jenkins, TeamCity, Gradle, Maven, Ant, JUnit e TestNG.
  • .NET Profiler – Perfilador de desempenho e memória fácil de usar para estrutura .NET.

Conclusão

Agora você sabe como os despejos de encadeamento são úteis para entender e diagnosticar problemas em aplicativos multiencadeados. Com adequado conhecimentoem relação aos thread dumps – sua estrutura, as informações contidas neles, e assim por diante – podemos utilizá-los para identificar rapidamente as causas dos problemas.