Existem alguns tópicos sobre os internos da JVM no programa de curso Java Developer . Entendemos os mecanismos de trabalho de coleções, bytecode, coletores de lixo, etc. Hoje, oferecemos sua atenção para a tradução de um artigo bastante interessante sobre despejo de threads. O que é, como obtê-lo e como usá-lo.Deseja aprender a analisar o despejo de threads? Vá abaixo do gato para saber mais sobre como obter despejo de encadeamento em Java e o que fazer com ele mais tarde.
A maioria dos aplicativos Java modernos são multithread. O multithreading pode expandir significativamente a funcionalidade do aplicativo, ao mesmo tempo em que introduz uma complexidade significativa.
Em um aplicativo de thread único, todos os recursos (memória compartilhada, operações de entrada / saída, etc.) podem ser usados sem sincronização, porque a qualquer momento, apenas um encadeamento usa o recurso.
No caso de aplicativos multithread, é necessário encontrar um compromisso entre complicar o programa e um possível aumento no desempenho, quando vários threads podem usar todo o CPU (CPU) disponível (geralmente mais de um). Se tudo for feito corretamente, usando o multithreading (formalizado na
Lei da Amdahl ), você poderá obter um aumento significativo no desempenho do aplicativo. No entanto, é preciso lembrar de fornecer acesso simultâneo de vários fluxos a um recurso compartilhado. Na maioria dos casos, estruturas como o Spring encapsulam o trabalho com threads e ocultam muitos detalhes técnicos dos usuários. No entanto, no caso de usar estruturas complexas modernas, algo pode dar errado, e nós, como usuários, encontraremos dificuldades para solucionar erros de multithreading.
Felizmente, o Java está equipado com um mecanismo especial para obter informações sobre o estado atual de todos os encadeamentos a qualquer momento - este é um dump de encadeamento (um tipo de captura instantânea). Neste artigo, aprenderemos como obter um despejo de segmento para um aplicativo de tamanho realista e como analisar esse despejo.
Supõe-se que o leitor tenha informações básicas sobre programação multithread e esteja ciente dos problemas de sincronização de threads e do uso de recursos compartilhados. No entanto, não será supérfluo atualizar alguns termos e conceitos básicos.
Terminologia básica
À primeira vista, os despejos de encadeamento Java podem parecer uma "letra chinesa", os seguintes conceitos são essenciais para entendê-lo. Em geral, vamos repetir os termos básicos de multithreading, que usaremos para analisar dumps.
- Thread ou thread é uma unidade multithreading discreta gerenciada pela Java Virtual Machine (JVM). Os encadeamentos da JVM correspondem aos encadeamentos no sistema operacional (SO) - encadeamentos nativos, que implementam o mecanismo de execução de código.
Cada encadeamento possui um identificador e nome exclusivos. Os fluxos podem ser "demônios" e "não demônios".
O programa termina quando todos os encadeamentos não daemon terminam ou o método Runtime.exit é chamado . Os "demônios" em funcionamento não afetam a conclusão do programa. I.e. A JVM está aguardando a finalização e o desligamento de todos os "não-demônios"; eles não estão prestando atenção aos "não-demônios".
Para mais informações, consulte a documentação da classe Thread .
Um fluxo pode estar em um dos seguintes estados:
- Tópico ativo ou "ativo" - um tópico que faz algum trabalho (estado normal).
- Encadeamento bloqueado ou "bloqueado" - um encadeamento que tentou entrar na seção sincronizada (sincronizada), mas outro encadeamento já conseguiu inserir esse bloco primeiro, e todos os encadeamentos a seguir que tentam inserir o mesmo bloco são bloqueados.
- Aguardando thread ou "waiting" - um thread que chamou o método wait (possivelmente com um tempo limite) e agora aguarda outro método para executar notify ou nonifyAll no mesmo objeto.
Observe que o encadeamento não é considerado "aguardando" se tiver chamado de espera com tempo limite e esse tempo limite expirar. - Encadeamento adormecido ou "adormecido" - um encadeamento que não está em execução no momento, porque executou o método Thread.sleep (indicando a duração do "sono").
- O monitor é um mecanismo usado pela JVM para fornecer acesso multiencadeado a um único objeto. O mecanismo é iniciado usando a palavra-chave sincronizada especial.
Cada objeto em Java possui um monitor com o qual o encadeamento pode ser sincronizado, ou seja, defina um bloqueio, o que garante que nenhum outro encadeamento tenha acesso a esse objeto até que o bloqueio seja liberado, ou seja, thread - o proprietário do bloqueio não sairá do bloco sincronizado .
Consulte a seção Sincronização (17.1) da Java Langauge Specification (JLS) para obter mais informações .
- Deadlock é uma situação em que um encadeamento, digamos A, bloqueia um recurso, ele precisa de outro recurso bloqueado por outro encadeamento, digamos B. O fluxo B não libera esse recurso, porque Para concluir uma determinada operação, ele precisa de um recurso bloqueado pelo encadeamento A. Acontece que o encadeamento A está aguardando o desbloqueio do recurso pelo encadeamento B, que está aguardando que outro recurso seja desbloqueado pelo encadeamento A. E, portanto, os encadeamentos estão esperando um pelo outro. Como resultado, o programa inteiro trava e aguarda que os threads sejam desbloqueados e continuem funcionando. Pode haver muitos encadeamentos em um conflito. Esse problema é conhecido como "Problema dos Filósofos do Jantar" .

- Livelock é uma situação em que o encadeamento A força o encadeamento B a executar alguma ação, que por sua vez faz com que o encadeamento A execute a ação inicial, que mais uma vez causa a ação do encadeamento B. É obtida uma dependência cíclica. Isso pode ser imaginado como um cachorro correndo atrás de sua cauda. Da mesma forma que Deadlock , em uma situação de Livelock, o programa não progride, ou seja, não executa uma ação útil, no entanto, nessa situação, os encadeamentos não são bloqueados.
A terminologia apresentada não é exaustiva para descrever o mundo do multithreading, mas isso é suficiente para começar a analisar despejos de encadeamento.
Informações mais detalhadas podem ser encontradas nestas fontes:
Seção 17 da simultaneidade JLS e
Java na práticaUsando esses conceitos simples sobre fluxo em Java, podemos criar um aplicativo de teste. Para esta aplicação, compilaremos o despejo de threads. Analisaremos o dump resultante e extrairemos informações úteis sobre os fluxos de aplicativos atuais.
Criando um programa de amostra
Antes de criar o despejo de encadeamento, precisamos desenvolver um aplicativo Java. O tradicional "olá, mundo!" muito simples para o nosso propósito, e um despejo de tamanho médio do aplicativo pode ser muito complicado para demonstrar. Com base nisso, criaremos um aplicativo bastante simples no qual dois threads são criados. E os tópicos entram em conflito:
public class DeadlockProgram { public static void main(String[] args) throws Exception { Object resourceA = new Object(); Object resourceB = new Object(); Thread threadLockingResourceAFirst = new Thread(new DeadlockRunnable(resourceA, resourceB)); Thread threadLockingResourceBFirst = new Thread(new DeadlockRunnable(resourceB, resourceA)); threadLockingResourceAFirst.start(); Thread.sleep(500); threadLockingResourceBFirst.start(); } private static class DeadlockRunnable implements Runnable { private final Object firstResource; private final Object secondResource; public DeadlockRunnable(Object firstResource, Object secondResource) { this.firstResource = firstResource; this.secondResource = secondResource; } @Override public void run() { try { synchronized(firstResource) { printLockedResource(firstResource); Thread.sleep(1000); synchronized(secondResource) { printLockedResource(secondResource); } } } catch (InterruptedException e) { System.out.println("Exception occurred: " + e); } } private static void printLockedResource(Object resource) { System.out.println(Thread.currentThread().getName() + ": locked resource -> " + resource); } } }
Este programa cria dois recursos: resourceA e resourceB e inicia dois threads: threadLockingResourceAFirst e threadLockingResourceBFirst, que bloqueiam os recursos um do outro.
A causa do conflito é um bloqueio "cruzado" de recursos por encadeamentos.
O motivo da ocorrência de conflito é uma tentativa de capturar recursos "mutuamente", ou seja, O encadeamento threadLockingResourceAFirst captura o recurso resourceA, o encadeamento threadLockingResourceBFirst captura o recurso resourceB. Depois disso, o threadLockingResourceAFirst, sem liberar seu recurso, tenta capturar o recurso B, e o threadLockingResourceBFirst, sem liberar seu recurso, tenta capturar o recursoA. Como resultado, os threads estão bloqueados. Um atraso de 1s foi adicionado para garantir o bloqueio. Os encadeamentos aguardam a liberação dos recursos necessários, mas isso nunca acontecerá.
A saída do programa será assim (os números após java.lang.Object @ serão diferentes para cada inicialização):
Thread-0: locked resource -> java.lang.Object@149bc794 Thread-1: locked resource -> java.lang.Object@17c10009
Após a saída dessas mensagens, o programa parecerá estar em execução (o processo de execução deste programa não foi concluído), enquanto o programa não realiza nenhum trabalho. É assim que o impasse parece na prática. Para resolver o problema, precisamos criar manualmente um despejo de piso e analisar o estado dos encadeamentos.
Geração de despejo de segmento
Na prática, um programa Java pode falhar ao criar um despejo de encadeamento. No entanto, em alguns casos (por exemplo, no caso de conflitos), o programa não termina e o despejo de encadeamentos não é criado, apenas trava. Para criar um despejo desses programas travados, primeiro, você precisa descobrir o identificador do processo do programa, ou seja, ID do processo (PID). Para fazer isso, é possível usar o utilitário JVM Process Status (JPS), que, começando com a versão 7, faz parte do Java Development Kit (JDK). Para encontrar o PID do processo do nosso programa travado, simplesmente executamos jps no terminal (Windows ou Linux):
$ jps 11568 DeadlockProgram 15584 Jps 15636
A primeira coluna é o identificador da máquina virtual local (ID da VM local, ou seja, lvmid) para o processo Java em execução. No contexto da JVM local, lvmid aponta para o PID do processo Java.
Deve-se notar que esse valor provavelmente difere do valor acima. A segunda coluna é o nome do aplicativo, que pode apontar para o nome da classe principal, arquivo jar ou igual a "Desconhecido". Tudo depende de como o aplicativo foi iniciado.
No nosso caso, o nome do aplicativo DeadlockProgram é o nome das principais classes que foram iniciadas quando o programa foi iniciado. No exemplo acima do PID do programa 11568, essas informações são suficientes para gerar um despejo de encadeamento. Para gerar o dump, usaremos o utilitário
jstack , que faz parte do JDK, começando na versão 7. Para obter o dump,
passaremos o PID do nosso programa para o
jstack e especificar o sinalizador -l (criando uma lista longa). A saída do utilitário será redirecionada para um arquivo de texto, ou seja, thread_dump.txt:
jstack -l 11568 > thread_dump.txt
O arquivo thread_dump.txt resultante contém o despejo de encadeamento do nosso programa travado e contém informações importantes para diagnosticar as causas do conflito.
Se o JDK for usado até a versão 7, para gerar um dump, você poderá usar o utilitário Linux -
kill com o sinalizador -3. Chamar kill -3 enviará ao programa um sinal SIGQUIT.
No nosso caso, a chamada será assim:
kill -3 11568
Análise simples de despejo de encadeamento
Abrindo o arquivo thread_dump.txt, veremos algo como o seguinte:
2018-06-19 16:44:44
VM do servidor Java HotSpot (TM) de 64 bits de despejo de thread completo (10.0.1 + 10 modo misto)
Informações de SMR da classe de threads:
_java_thread_list = 0x00000250e5488a00, length = 13, elementos = {
0x00000250e4979000, 0x00000250e4982800, 0x00000250e52f2800, 0x00000250e4992800,
0x00000250e4995800, 0x00000250e49a5800, 0x00000250e49ae800, 0x00000250e5324000,
0x00000250e54cd800, 0x00000250e54cf000, 0x00000250e54d1800, 0x00000250e54d2000,
0x00000250e54d0800
}
"Daemon Handler de referência" nº 2 prio = 10 os_prio = 2id = 0x00000250e4979000 nid = 0x3c28 aguardando condição [0x000000b82a9ff000]
java.lang.Thread.State: RUNNABLE
em java.lang.ref.Reference.waitForReferencePendingList (java.base@10.0.1/Native Method)
em java.lang.ref.Reference.processPendingReferences (java.base@10.0.1/Reference.java: 174)
em java.lang.ref.Reference.access $ 000 (java.base@10.0.1/Reference.java: 44)
em java.lang.ref.Reference $ ReferenceHandler.run (java.base@10.0.1/Reference.java: 138)
Sincronizadores proprietários bloqueados:
- nenhum
"Daemon finalizador" nº 3 prio = 8 os_prio = 1 vez = 0x00000250e4982800 nid = 0x2a54 em Object.wait () [0x000000b82aaff000]
java.lang.Thread.State: WAITING (no monitor de objeto)
em java.lang.Object.wait (java.base@10.0.1/Native Method)
- aguardando <0x0000000089509410> (um bloqueio de java.lang.ref.ReferenceQueue $)
em java.lang.ref.ReferenceQueue.remove (java.base@10.0.1/ReferenceQueue.java: 151)
- aguardando para bloquear novamente em wait () <0x0000000089509410> (um java.lang.ref.ReferenceQueue $ Lock)
em java.lang.ref.ReferenceQueue.remove (java.base@10.0.1/ReferenceQueue.java: 172)
em java.lang.ref.Finalizer $ FinalizerThread.run (java.base@10.0.1/Finalizer.java: 216)
Sincronizadores proprietários bloqueados:
- nenhum
"Signal Dispatcher" # 4 daemon prio = 9 os_prio = 2 vezes = 0x00000250e52f2800 nid = 0x2184 executável [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Sincronizadores proprietários bloqueados:
- nenhum
Daemon "Attach Listener" nº 5 prio = 5 os_prio = 2 vezes = 0x00000250e4992800 nid = 0x1624 aguardando condição [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Sincronizadores proprietários bloqueados:
- nenhum
"C2 CompilerThread0" # 6 daemon prio = 9 os_prio = 2id = 0x00000250e4995800 nid = 0x4198 aguardando condição [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Nenhuma tarefa de compilação
Sincronizadores proprietários bloqueados:
- nenhum
"C2 CompilerThread1" # 7 daemon prio = 9 os_prio = 2id = 0x00000250e49a5800 nid = 0x3b98 aguardando condição [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Nenhuma tarefa de compilação
Sincronizadores proprietários bloqueados:
- nenhum
"C1 CompilerThread2" # 8 daemon prio = 9 os_prio = 2id = 0x00000250e49ae800 nid = 0x1a84 aguardando condição [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Nenhuma tarefa de compilação
Sincronizadores proprietários bloqueados:
- nenhum
"Sweeper thread" nº 9 daemon prio = 9 os_prio = 2 vezes = 0x00000250e5324000 nid = 0x5f0 executável [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Sincronizadores proprietários bloqueados:
- nenhum
"Daemon de serviço" nº 10 prio = 9 os_prio = 0 por dia = 0x00000250e54cd800 nid = 0x169c executável [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Sincronizadores proprietários bloqueados:
- nenhum
"Common-Cleaner" nº 11 daemon prio = 8 os_prio = 1 vez = 0x00000250e54cf000 nid = 0x1610 em Object.wait () [0x000000b82b2fe000]
java.lang.Thread.State: TIMED_WAITING (no monitor de objeto)
em java.lang.Object.wait (java.base@10.0.1/Native Method)
- aguardando <0x000000008943e600> (um bloqueio de java.lang.ref.ReferenceQueue $)
em java.lang.ref.ReferenceQueue.remove (java.base@10.0.1/ReferenceQueue.java: 151)
- aguardando para bloquear novamente em wait () <0x000000008943e600> (um java.lang.ref.ReferenceQueue $ Lock)
em jdk.internal.ref.CleanerImpl.run (java.base@10.0.1/CleanerImpl.java: 148)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
em jdk.internal.misc.InnocuousThread.run (java.base@10.0.1/InnocuousThread.java: 134)
Sincronizadores proprietários bloqueados:
- nenhum
"Thread-0" # 12 prio = 5 os_prio = 0 detalhe = 0x00000250e54d1800 nid = 0xdec aguardando entrada do monitor [0x000000b82b4ff000]
java.lang.Thread.State: BLOCKED (no monitor de objeto)
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465b0> (um java.lang.Object)
- bloqueado <0x00000000894465a0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
Sincronizadores proprietários bloqueados:
- nenhum
"Thread-1" # 13 prio = 5 os_prio = 0 por dia = 0x00000250e54d2000 nid = 0x415c aguardando entrada do monitor [0x000000b82b5ff000]
java.lang.Thread.State: BLOCKED (no monitor de objeto)
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465a0> (um java.lang.Object)
- bloqueado <0x00000000894465b0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
Sincronizadores proprietários bloqueados:
- nenhum
"DestroyJavaVM" # 14 prio = 5 os_prio = 0 maré = 0x00000250e54d0800 nid = 0x2b8c aguardando condição [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Sincronizadores proprietários bloqueados:
- nenhum
"Thread da VM" os_prio = 2id = 0x00000250e496d800 nid = 0x1920 executável
"Tópico do GC # 0" os_prio = 2id = 0x00000250c35b5800 nid = 0x310c executável
"Tópico do GC # 1" os_prio = 2id = 0x00000250c35b8000 nid = 0x12b4 executável
"Tópico do GC # 2" os_prio = 2id = 0x00000250c35ba800 nid = 0x43f8 executável
"Tópico do GC # 3" os_prio = 2id = 0x00000250c35c0800 nid = 0x20c0 executável
"Marcador principal G1" os_prio = 2 por dia = 0x00000250c3633000 nid = 0x4068 executável
"G1 Conc # 0" os_prio = 2id = 0x00000250c3636000 nid = 0x3e28 executável
"G1 Refine # 0" os_prio = 2 por dia = 0x00000250c367e000 nid = 0x3c0c executável
"G1 Refine # 1" os_prio = 2id = 0x00000250e47fb800 nid = 0x3890 executável
"G1 Refine # 2" os_prio = 2id = 0x00000250e47fc000 nid = 0x32a8 executável
"G1 Refine # 3" os_prio = 2id = 0x00000250e47fd800 nid = 0x3d00 executável
"G1 Young RemSet Sampling" os_prio = 2 por dia = 0x00000250e4800800 nid = 0xef4 executável
"Thread de tarefa periódica da VM" os_prio = 2id = 0x00000250e54d6800 nid = 0x3468 aguardando condição
Referências globais da JNI: 2
Encontrou um impasse no nível do Java:
===============================
"Thread-0":
aguardando para bloquear o monitor 0x00000250e4982480 (objeto 0x00000000894465b0, um java.lang.Object),
que é mantido pelo "Thread-1"
"Thread-1":
aguardando para bloquear o monitor 0x00000250e4982380 (objeto 0x00000000894465a0, um java.lang.Object),
que é mantido por "Thread-0"
Informações da pilha Java para os encadeamentos listados acima:
===================================================== =
"Thread-0":
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465b0> (um java.lang.Object)
- bloqueado <0x00000000894465a0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
"Thread-1":
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465a0> (um java.lang.Object)
- bloqueado <0x00000000894465b0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
Encontrado 1 impasse.
Informações introdutórias
Embora, à primeira vista, esse arquivo possa parecer muito complicado e confuso, na realidade é bastante simples se você o desmontar em partes, passo a passo.
A primeira linha indica a hora em que o dump foi formado, a segunda - informações de diagnóstico sobre a JVM, na qual o dump foi recebido:
2018-06-19 16:44:44 Full thread dump Java HotSpot(TM) 64-Bit Server VM (10.0.1+10 mixed mode):
Não há informações de fluxo nesta seção. Aqui, o contexto geral do sistema no qual o dump foi coletado é definido.
Informações gerais sobre fluxo
A seção a seguir fornece informações sobre os threads que estavam em execução no sistema no momento da coleção de despejo:
Informações de SMR da classe de threads:
_java_thread_list = 0x00000250e5488a00, length = 13, elementos = {
0x00000250e4979000, 0x00000250e4982800, 0x00000250e52f2800, 0x00000250e4992800,
0x00000250e4995800, 0x00000250e49a5800, 0x00000250e49ae800, 0x00000250e5324000,
0x00000250e54cd800, 0x00000250e54cf000, 0x00000250e54d1800, 0x00000250e54d2000,
0x00000250e54d0800
}
A seção a seguir lista:
Informações sobre recuperação de memória segura (SMR)Ele contém informações sobre encadeamentos fora da JVM, ou seja, esses não são threads de máquina virtual ou de coleta de lixo. Se você observar os endereços desses encadeamentos, notará que eles correspondem ao valor de
tid - o endereço "natural, de ferro" (nativo) no sistema operacional, e não o ID do encadeamento.
Elipses são usadas para ocultar informações redundantes:
"Manipulador de referência" # 2 ... tid = 0x00000250e4979000 ...
"Finalizador" # 3 ... tid = 0x00000250e4982800 ...
"Distribuidor de sinal" # 4 ... tid = 0x00000250e52f2800 ...
"Anexar ouvinte" # 5 ... tid = 0x00000250e4992800 ...
"C2 CompilerThread0" # 6 ... tid = 0x00000250e4995800 ...
"C2 CompilerThread1" # 7 ... tid = 0x00000250e49a5800 ...
"C1 CompilerThread2" # 8 ... tid = 0x00000250e49ae800 ...
"Fio da vassoura" # 9 ... tid = 0x00000250e5324000 ...
"Tópico de serviço" # 10 ... tid = 0x00000250e54cd800 ...
"Limpador comum" # 11 ... tid = 0x00000250e54cf000 ...
"Tópico-0" # 12 ... tid = 0x00000250e54d1800 ...
"Tópico-1" # 13 ... tid = 0x00000250e54d2000 ...
"DestroyJavaVM" # 14 ... tid = 0x00000250e54d0800 ...
Streams
Logo após o bloco SMR, há uma lista de threads. O primeiro segmento da nossa lista é o manipulador de referência:
"Daemon Handler de referência" nº 2 prio = 10 os_prio = 2id = 0x00000250e4979000 nid = 0x3c28 aguardando condição [0x000000b82a9ff000]
java.lang.Thread.State: RUNNABLE
em java.lang.ref.Reference.waitForReferencePendingList (java.base@10.0.1/Native Method)
em java.lang.ref.Reference.processPendingReferences (java.base@10.0.1/Reference.java: 174)
em java.lang.ref.Reference.access $ 000 (java.base@10.0.1/Reference.java: 44)
em java.lang.ref.Reference $ ReferenceHandler.run (java.base@10.0.1/Reference.java: 138)
Sincronizadores proprietários bloqueados:
- nenhum
Breve descrição do fluxo
A primeira linha de cada thread fornece uma descrição geral. A descrição contém os seguintes itens:
Seção | Exemplo | Descrição do produto |
---|
Nome | "Manipulador de referência" | Nome do fluxo legível por humanos. O nome pode ser especificado chamando o método setName do objeto Thread . E receba uma ligação para getName |
ID | # 2 | Um ID exclusivo atribuído a cada objeto da classe Thread . O ID é gerado para threads no sistema. O valor inicial é 1. Cada thread recém-criado recebe seu próprio ID, aumentado anteriormente em 1. Essa propriedade de thread somente leitura pode ser obtida usando a função getId de um objeto da classe Thread . |
Status do daemon | daemon | A bandeira é um sinal de que o fio é um demônio. Se for um demônio, a bandeira será definida. Por exemplo, o encadeamento -0 não é um daemon. |
Prioridade | prio = 10 | A prioridade numérica do fluxo Java. Observe que essa prioridade não corresponde necessariamente à prioridade do encadeamento associado no sistema operacional. Para definir a prioridade, você pode use o método setPriority de um objeto da classe Thread e para obter método getPriority . |
Prioridade do encadeamento do SO | os_prio = 2 | Encadeamento prioritário no sistema operacional. Essa prioridade pode ser diferente da designada ao encadeamento Java vinculado. |
Morada | tid = 0x00000250e4979000 | O endereço do fluxo Java. Este endereço é um ponteiro para o objeto nativo Java Native Interface (JNI) da classe Thread (um objeto C ++ Thread conectado ao thread Java através da JNI). Este valor é obtido lançando um ponteiro para este (o objeto C ++ que está associado a esse encadeamento Java) em número inteiro. Veja linha 879 no ponto de acesso / compartilhamento / tempo de execução / thread.cpp :
st-> print ("tid =" INTPTR_FORMAT "", p2i (isto));
Embora a chave desse objeto ( tid ) possa parecer um ID de fluxo, de fato, este é o endereço do objeto conectado ao Thread JNI C ++ e este não é o valor que retorna o método getId do encadeamento Java. |
ID do encadeamento do SO | nid = 0x3c28 | O identificador exclusivo do encadeamento do sistema operacional ao qual o encadeamento Java está vinculado. Este valor é gerado com o seguinte código: linha 42 no ponto de acesso / compartilhamento / tempo de execução / osThread.cpp :
st-> print ("nid = 0x% x", thread_id ());
|
Status | esperando sob condição | Status legível humano do encadeamento atual. Essa linha exibe informações adicionais sobre o status simples do fluxo (veja abaixo), que pode ser usado para entender o que o encadeamento iria fazer (ou seja, se o encadeamento estava tentando obter um bloqueio ou aguardando a condição de desbloqueio). |
Último ponteiro de pilha Java conhecido | [0x000000b82a9ff000] | O último ponteiro de pilha conhecido (SP) associado a esse fluxo. Este valor é obtido usando o código C ++ nativo combinado com o código Java usando JNI. O valor é retornado pela função last_Java_sp () , linha 2886 no ponto de acesso / compartilhamento / tempo de execução / thread.cpp :
st-> print_cr ("[" INTPTR_FORMAT "]",
(intptr_t) last_Java_sp () & ~ right_n_bits (12));
Para despejos de encadeamento simples, essas informações são quase inúteis. Entretanto, em casos complexos, SP pode ser usado para rastrear bloqueios. |
Status do fluxo
A segunda linha é o estado atual do fluxo. Os possíveis estados do fluxo estão listados na enumeração:
Thread.State :
Novo
RUNNABLE
BLOQUEADO
ESPERANDO
TIMED_WAITING
TERMINADO
Veja a
documentação para mais detalhes.
Rastreio de Pilha de Encadeamentos
A próxima seção contém o rastreamento de pilha do fluxo no momento em que o despejo foi realizado. Esse rastreamento de pilha é muito semelhante a um rastreamento de pilha, lançado por uma exceção não capturada. E contém os nomes de classes e seqüências de caracteres que foram executadas no momento em que o despejo foi formado. No caso do fluxo do manipulador de referência, não vemos nada de interessante.
No entanto, há algo interessante no rastreamento de encadeamento do Thread-02 que é diferente do rastreamento padrão:
"Thread-0" # 12 prio = 5 os_prio = 0 detalhe = 0x00000250e54d1800 nid = 0xdec aguardando entrada do monitor [0x000000b82b4ff000]
java.lang.Thread.State: BLOCKED (no monitor de objeto)
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465b0> (um java.lang.Object)
- bloqueado <0x00000000894465a0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
Sincronizadores proprietários bloqueados:
- nenhum
No rastreamento, vemos que as informações sobre o bloqueio foram adicionadas. Esse encadeamento espera um bloqueio no objeto com o endereço 0x00000000894465b0 (tipo de objeto java.lang.Object). Além disso, o próprio encadeamento mantém o bloqueio com o endereço 0x00000000894465a0 (também um java.lang.Object). Essas informações nos serão úteis posteriormente para o diagnóstico de conflito.
Primitivas de sincronização capturadas (Ownable Synchronizer)
A última seção lista as primitivas de sincronização capturadas pelo fluxo. Esses são objetos que podem ser usados para sincronizar threads, por exemplo, bloqueios.
De acordo com a documentação oficial do Java, o
Ownable Synchronizer é o descendente do
AbstractOwnableSynchronizer (ou sua subclasse), que pode ser capturado exclusivamente pelo fluxo para fins de sincronização.
ReentrantLock e
bloqueio de gravação , mas não o
bloqueio de
leitura da classe
ReentrantReadWriteLock, são dois bons exemplos desses "sincronizadores proprietários" oferecidos pela plataforma.
Para mais informações sobre este assunto, você pode consultar este
post .
Encadeamentos JVM
A próxima seção do dump contém informações sobre os encadeamentos técnicos da JVM que não fazem parte do aplicativo e estão associados aos encadeamentos do sistema operacional. Porque esses fluxos funcionam fora do aplicativo, eles não têm identificadores de fluxo. Na maioria das vezes, esses são threads do coletor de lixo e outros threads técnicos da JVM:
"Thread da VM" os_prio = 2id = 0x00000250e496d800 nid = 0x1920 executável
"Tópico do GC # 0" os_prio = 2id = 0x00000250c35b5800 nid = 0x310c executável
"Tópico do GC # 1" os_prio = 2id = 0x00000250c35b8000 nid = 0x12b4 executável
"Tópico do GC # 2" os_prio = 2id = 0x00000250c35ba800 nid = 0x43f8 executável
"Tópico do GC # 3" os_prio = 2id = 0x00000250c35c0800 nid = 0x20c0 executável
"Marcador principal G1" os_prio = 2 por dia = 0x00000250c3633000 nid = 0x4068 executável
"G1 Conc # 0" os_prio = 2id = 0x00000250c3636000 nid = 0x3e28 executável
"G1 Refine # 0" os_prio = 2 por dia = 0x00000250c367e000 nid = 0x3c0c executável
"G1 Refine # 1" os_prio = 2id = 0x00000250e47fb800 nid = 0x3890 executável
"G1 Refine # 2" os_prio = 2id = 0x00000250e47fc000 nid = 0x32a8 executável
"G1 Refine # 3" os_prio = 2id = 0x00000250e47fd800 nid = 0x3d00 executável
"G1 Young RemSet Sampling" os_prio = 2 por dia = 0x00000250e4800800 nid = 0xef4 executável
"Thread de tarefa periódica da VM" os_prio = 2id = 0x00000250e54d6800 nid = 0x3468 aguardando condição
JNI Global Links
Esta seção indica o número de referências globais usadas pela JVM através da JNI. Esses links não são atendidos pelo coletor de lixo e podem causar vazamento de memória em determinadas circunstâncias.
Referências globais da JNI: 2
Na maioria dos casos simples, essas informações não são usadas. No entanto, a importância das referências globais deve ser entendida. Veja este
post para mais detalhes.
Tópicos com deadlock
A última seção contém informações sobre conflitos encontrados.
Se estes não forem encontrados, a seção estará vazia. Porque Desenvolvemos um aplicativo especificamente com bloqueios. No nosso caso, esta seção é. Um bloqueio foi detectado durante o dumping e é apresentado com a seguinte mensagem:
Encontrou um impasse no nível do Java:
===============================
"Thread-0":
aguardando para bloquear o monitor 0x00000250e4982480 (objeto 0x00000000894465b0, um java.lang.Object),
que é mantido pelo "Thread-1"
"Thread-1":
aguardando para bloquear o monitor 0x00000250e4982380 (objeto 0x00000000894465a0, um java.lang.Object),
que é mantido por "Thread-0"
Informações da pilha Java para os encadeamentos listados acima:
===================================================== =
"Thread-0":
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465b0> (um java.lang.Object)
- bloqueado <0x00000000894465a0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
"Thread-1":
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34)
- aguardando para bloquear <0x00000000894465a0> (um java.lang.Object)
- bloqueado <0x00000000894465b0> (um java.lang.Object)
em java.lang.Thread.run (java.base@10.0.1/Thread.java: 844)
Encontrado 1 impasse.
A primeira subseção descreve o cenário de conflito:
O thread-0 espera a capacidade de capturar um monitor (isso está acessando o bloco
sincronizado (secondResource) em nosso aplicativo), enquanto este thread está segurando um monitor que está tentando capturar o thread-1 (está acessando o mesmo fragmento de código:
) em nossa aplicação).
Esse bloqueio circular também é chamado de
deadlock . Na imagem abaixo
esta situação é apresentada em forma gráfica:

Na segunda subseção, o rastreamento de pilha é fornecido para os dois threads bloqueados.
Esse rastreamento de pilha permite rastrear a operação de cada encadeamento até que ocorra um bloqueio.
No nosso caso, se olharmos para a linha:
em DeadlockProgram $ DeadlockRunnable.run (DeadlockProgram.java:34) , veremos o problema como parte do código:
printLockedResource (secondResource);
Essa linha é a primeira linha do bloco sincronizado, que é o motivo do bloqueio, e informa que a sincronização no secondResource é o motivo do bloqueio mútuo. Para remediar a situação, devemos garantir que os dois threads tenham a mesma ordem de sincronização nos recursos resourceA e resourceB. Se fizermos isso, chegaremos ao seguinte aplicativo:
public class DeadlockProgram { public static void main(String[] args) throws Exception { Object resourceA = new Object(); Object resourceB = new Object(); Thread threadLockingResourceAFirst = new Thread(new DeadlockRunnable(resourceA, resourceB)); Thread threadLockingResourceBFirst = new Thread(new DeadlockRunnable(resourceA, resourceB)); threadLockingResourceAFirst.start(); Thread.sleep(500); threadLockingResourceBFirst.start(); } private static class DeadlockRunnable implements Runnable { private final Object firstResource; private final Object secondResource; public DeadlockRunnable(Object firstResource, Object secondResource) { this.firstResource = firstResource; this.secondResource = secondResource; } @Override public void run() { try { synchronized (firstResource) { printLockedResource(firstResource); Thread.sleep(1000); synchronized (secondResource) { printLockedResource(secondResource); } } } catch (InterruptedException e) { System.out.println("Exception occurred: " + e); } } private static void printLockedResource(Object resource) { System.out.println(Thread.currentThread().getName() + ": locked resource -> " + resource); } } }
Este aplicativo terminará sem intertravamento e, como resultado, obteremos a seguinte saída (observe que os endereços da classe Object foram alterados):
Thread-0: recurso bloqueado -> java.lang.Object@1ad895d1
Thread-0: recurso bloqueado -> java.lang.Object@6e41d7dd
Tópico-1: recurso bloqueado -> java.lang.Object@1ad895d1
Tópico-1: recurso bloqueado -> java.lang.Object@6e41d7dd
, , thread dump, . ( deadlock-). , .
Thread Dump-
.
JVM . ( , ).
.
- — Thread Dump Analyzers (TDAs). Java thread dump- - , . , . , .
TDA:
. .
Conclusão
Thread dump- — Java-, . , .
deadlock, . . , — .
, Java- thread dump-. , .
, thread dump — « » , , Java-.
Java . , deadlock- ., .