Resposta rápida
Um analisador de dump de virtual threads lê a saída do jcmd <pid> Thread.dump_to_file e diz o que as suas virtual threads estão fazendo: quais estão pinadas a um carrier, quais estão mounted, unmounted ou aguardando. O jstack comum não as lista.
Gere o dump com o comando abaixo e cole ou envie o arquivo na página de análise. Sem cadastro para o primeiro dump.
jcmd <pid> Thread.dump_to_file -format=json /tmp/vthreads.json
Como tirar um thread dump com virtual threads
As ferramentas clássicas, jstack <pid> e jcmd <pid> Thread.print, imprimem as platform threads. Um serviço que roda em virtual threads pode ter centenas de milhares delas, e elas não saem desses comandos como threads próprias. A partir do JDK 21, Thread.dump_to_file grava um dump que as inclui, em um de dois formatos:
# Texto simples, na família de layout do jstack: jcmd <pid> Thread.dump_to_file -format=text /tmp/vthreads.txt # JSON, com containers de threads e, no JDK 25, estado e carrier: jcmd <pid> Thread.dump_to_file -format=json /tmp/vthreads.json
O arquivo é escrito pela JVM alvo, então o caminho precisa ser gravável por esse processo, e não pelo usuário que roda o jcmd. Para as outras formas de capturar um dump (kill -3, containers, Windows), veja como tirar um thread dump.
O que o ThreadMine mostra para virtual threads
- Pinning, pelo sinal real. Uma virtual thread é reportada como pinada quando a pilha dela tem o frame
java.lang.VirtualThread.parkOnCarrierThread. Sem adivinhar por nome de thread ou marcadores. Uma ou duas virtual threads pinadas são um aviso; três ou mais são críticas. O achado lista os carriers envolvidos quando o dump os informa. Por que acontece e como corrigir: virtual thread pinning. - Mounted, unmounted e aguardando. O painel de Virtual Threads conta o total e o divide em Mounted, Unmounted, Waiting e Pinned.
- Agrupadas por carrier. Quando o dump diz em qual carrier cada virtual thread roda, uma tabela as agrupa por carrier, com as pinadas primeiro. Clique num carrier para listar as virtual threads dele.
- Identidade pelo id da thread. Virtual threads costumam não ter nome, então uma sem nome aparece como
virtual #<tid>em vez de ser fundida com as outras. - Dumps grandes. O texto e o JSON são lidos thread a thread, e dumps com mais de 500.000 virtual threads são analisados por amostragem, com um aviso no relatório. As contagens por estado continuam cobrindo todas.
Texto ou JSON?
Os dois são aceitos, colados ou como arquivo (.txt, .log, .json, ou um .zip / .gz com um deles dentro). O JSON descreve o dump como containers de threads, cada um com as suas threads, e traz mais estrutura:
- JDK 25: cada virtual thread vem com estado e carrier, então a tabela de carriers é exata.
- JDK 22 a 24: o JSON não tem estado, flag de virtual nem carrier. O ThreadMine deduz o estado pelo topo da pilha e reconhece uma virtual thread pela base dela.
- Containers: o JSON lista o container de cada thread (executor, ForkJoinPool, o container raiz). O ThreadMine os lê para os dados da análise, mas o relatório não os exibe em gráfico.
Sem preferência, use JSON no JDK 25 e texto nos JDKs mais antigos. Dump com dados sensíveis? Mascare localmente antes.
Achados comuns
Pinning em synchronized ou Object.wait (JDK 23 e anteriores)
Uma virtual thread que bloqueia dentro de um bloco synchronized ou em Object.wait() mantém o carrier até o JDK 23. No dump ela aparece com o frame parkOnCarrierThread, e o contador de pinadas fica âmbar. O JDK 24 (JEP 491) removeu esse caso; frames nativos e de foreign function ainda podem pinar.
Fome de carriers
O pool de carriers padrão tem cerca de uma thread por núcleo de CPU. Quando, na tabela de carriers, todo carrier tem virtual threads pinadas e a aplicação para de responder, o scheduler não tem onde rodar as demais. Parece um deadlock, mas não é um ciclo de locks, e por isso a tabela de carriers é o lugar para conferir.
Virtual thread presa em I/O
Um dump mostra uma virtual thread esperando num socket, não há quanto tempo. Para distinguir presa de apenas ocupada, tire dois dumps com alguns segundos de intervalo e compare quais virtual threads estão no mesmo frame nos dois. O guia de análise de thread dump mostra como ler isso.
Mais sobre a leitura geral de um dump: analisador de thread dump Java.
Perguntas frequentes
O jstack mostra virtual threads?
Não. O jstack e o jcmd Thread.print listam as platform threads; as virtual threads não são enumeradas como threads próprias. Para obtê-las, use jcmd <pid> Thread.dump_to_file, disponível a partir do JDK 21, em formato texto ou JSON.
Qual comando tira um dump de virtual threads?
jcmd <pid> Thread.dump_to_file -format=text <arquivo> ou jcmd <pid> Thread.dump_to_file -format=json <arquivo>. O ThreadMine lê os dois. Cole o conteúdo do arquivo ou envie-o na página de análise.
Como o ThreadMine decide que uma virtual thread está pinada?
Pelo frame java.lang.VirtualThread.parkOnCarrierThread na pilha dela, o frame que a JVM mostra quando uma virtual thread estaciona ainda segurando o carrier. Se esse frame não está no dump, nenhum pinning é reportado. Uma ou duas virtual threads pinadas geram um aviso; três ou mais geram um achado crítico.
Por que minhas virtual threads aparecem sem nome?
Virtual threads são criadas sem nome, a menos que o seu código dê um, então o dump mostra o nome vazio. O ThreadMine identifica uma virtual thread sem nome pelo id da thread e a exibe como "virtual #<tid>", para que threads sem nome não sejam fundidas numa entrada só.
Texto ou JSON: qual formato usar?
Os dois funcionam. O JSON também lista os containers de threads (executors, ForkJoinPools) e, no JDK 25, o estado e o carrier de cada virtual thread. No JDK 22 a 24 o JSON não tem campo de estado nem de carrier, e o ThreadMine deduz o estado pelo topo de cada pilha.
Quantas virtual threads posso analisar?
Até 500.000 virtual threads mantêm pilha e locks completos. Acima disso o dump é analisado por amostragem: os totais por estado continuam cobrindo todas as virtual threads, mas só as primeiras 500.000 mantêm o detalhe, e o relatório avisa. O limite de tamanho de upload mostrado na caixa de envio também vale.
Um único dump mostra uma virtual thread presa em I/O?
Não de forma confiável. Um dump é um instante: mostra a virtual thread esperando, não há quanto tempo. Tire dois dumps com alguns segundos de intervalo e veja quais virtual threads estão no mesmo ponto nos dois.
Tem um dump de virtual threads?
Cole e veja as virtual threads pinadas e os carriers que as seguram. Sem cadastro.