Um thread dump responde "o que a JVM está fazendo agora?" — e a maioria dos problemas que valem a pena capturar não é sobre o agora. Um thread leak cresce ao longo de horas. Um pool de conexões satura gradualmente conforme o tráfego sobe. Um lock que está bem a 100 req/s vira ponto de serialização a 400. Diagnosticar isso exige uma série de dumps — e, mais importante, dumps tirados no momento certo. Um snapshot é uma foto; monitoramento é o filme. Este guia percorre os três caminhos que os times usam para ir de um ao outro: um script de cron, o gravador de voo da própria JVM e a captura por gatilho — incluindo o que cada abordagem faz genuinamente bem.
Um dump não é monitoramento
Um dump sozinho prova um deadlock (a própria JVM imprime o ciclo) e mostra uma exaustão óbvia se você o pegar no meio do incidente. Todo o resto que importa é um delta:
- Thread leaks são uma contagem que sobe entre dumps — um snapshot com 900 threads não significa nada até você saber que eram 300 uma hora atrás.
- Travado vs lento é a mesma pergunta ao longo do tempo: uma thread congelada no mesmo frame e lock em três dumps está travada; uma que se move está só ocupada.
- Saturação de pool tem hora de início — a pergunta útil no postmortem é quando o pool exec bateu no teto, o que exige dumps de antes do incidente, não só de durante.
Há um segundo problema, menos óbvio: quando um humano percebe o incidente e entra por SSH com o jstack, o momento interessante — o pico, o primeiro minuto da degradação — muitas vezes já passou. Captura manual está sempre atrasada pelo tamanho do seu tempo de reação. Monitoramento existe para eliminar esse atraso.
A rota manual: cron + jstack
A coisa mais simples que funciona: um script que dumpa num intervalo e rotaciona arquivos antigos.
#!/bin/sh # /etc/cron.d/thread-dumps: a cada 5 min, retem 24h PID=$(pgrep -f 'org.apache.catalina.startup.Bootstrap') [ -n "$PID" ] && jstack -l "$PID" > "/var/dumps/dump-$(date +%Y%m%d-%H%M%S).txt" find /var/dumps -name 'dump-*.txt' -mmin +1440 -delete
Isso é honestamente melhor que nada, e para uma investigação de fogo lento (um leak ao longo de um fim de semana) serve. Os limites, porém, são estruturais:
- Intervalos fixos perdem o momento. Um pico de CPU de 30 segundos entre dois ticks de 5 minutos não deixa rastro. Encolher o intervalo multiplica arquivos, não entendimento.
- Ninguém os lê. Centenas de arquivos de dump se acumulam sem leitura; quando o incidente chega, alguém os grepa na mão, sob pressão.
- A correlação é manual. Casar os timestamps dos dumps com a linha do tempo do alerta, o log de deploy e o gráfico de tráfego é trabalho braçal que o script não faz por você.
JFR: o gravador embutido da JVM
O Java Flight Recorder é a resposta nativa da JVM para "grave o tempo todo": um ring buffer contínuo e de baixo overhead (~1%) de eventos do runtime que você pode deixar ligado em produção e extrair quando algo acontece.
# inicia uma gravacao continua numa JVM rodando $ jcmd 41317 JFR.start name=continua maxage=24h # depois de um incidente, extrai a janela que importa $ jcmd 41317 JFR.dump name=continua filename=incidente.jfr
Para análise de threads, o JFR grava amostras de execução, eventos de contenção de lock e (em JDKs modernos) eventos periódicos de thread dump. É o default certo para perguntas de profiling — para onde vai o tempo de CPU, quais locks disputam numa janela. O trade-off é o fluxo de trabalho: os dados vivem num arquivo binário .jfr que você abre no JDK Mission Control e interpreta por conta própria. Ele grava tudo e não diagnostica nada — o que é ótimo se ler flame graphs é a sua ideia de diversão, e um custo real se o que você precisa às 3h da manhã é "me diga o que está errado".
Quando automatizar — e por que gatilhos vencem intervalos
O sinal de que você cresceu além da rota manual é a repetição: a mesma classe de incidente voltando (picos intermitentes de CPU, um travamento a cada poucas semanas), postmortems que terminam com "não tínhamos um dump do momento certo", ou um ambiente de produção onde o acesso SSH é lento ou controlado. Nesse ponto a pergunta não é se automatizar, e sim o que deve puxar o gatilho:
- Captura por intervalo (o cron acima) amostra o tempo uniformemente — a maioria dos dumps é tediosa, e o interessante pode nem existir.
- Captura por gatilho vigia os sinais vitais da própria JVM e dumpa quando eles cruzam um threshold — cada dump existe porque algo estava errado naquele instante. A JVM expõe todos os sinais de graça via JMX: CPU do processo, um detector de deadlock embutido (
findDeadlockedThreads), ocupação de heap, tempos de pausa de GC.
Captura por gatilho é para onde as ferramentas desse espaço convergem, e é a abordagem que vale construir ou comprar: o dump dos primeiros segundos do incidente é o que mostra a causa, não o rescaldo.
Captura por gatilho com o agente do ThreadMine
O agente de captura do ThreadMine é esse padrão empacotado: um agente Java standalone (um JAR de ~1,3 MB, Java 11+, sem dependências de framework) que se anexa a uma JVM alvo no mesmo host, observa os MBeans nativos dela a cada 10 segundos e captura um dump quando um gatilho dispara:
- CPU alta sustentada — CPU do processo acima de um threshold (default 80%) por várias leituras consecutivas, para um pico momentâneo não disparar.
- Deadlock — o próprio
findDeadlockedThreadsda JVM; raro e crítico, então esse gatilho fura o cooldown global que silencia os demais. - Heap saturado — uso cravado perto do máximo (default 95%) em verificações consecutivas.
- Pausas longas de GC — pausa média numa janela móvel (opt-in, já que a linha de base saudável varia muito entre aplicações).
Cooldowns impedem que um incidente persistente produza uma tempestade de dumps, e gatilhos simultâneos produzem um único dump rotulado com cada motivo (ex. [DEADLOCK] [HEAP 96%]). A configuração é gerada no painel web — JVM alvo, thresholds, projeto e tags — e baixada como um pacote pronto para rodar, autenticado por API key.
A metade que faz a captura valer a automação é o que acontece depois: cada dump é analisado na chegada pelo mesmo engine do upload manual — um conjunto de detectores de deadlock, pool exhaustion, thread leak e mais, além de um health score A–F — e capturas consecutivas se alinham numa timeline que mostra quando a degradação começou. O agente vem com o plano Pro (a partir de US$ 9/mês); se você quer ver o lado da análise antes de automatizar qualquer coisa, o analisador de thread dump online gratuito recebe o seu primeiro dump sem cadastro, e o guia completo de análise de thread dump ensina a ler os resultados.
Conclusão
Monitorar thread dumps é fechar duas lacunas que um jstack solitário deixa abertas: a tendência (leaks, saturação e locks quentes só existem entre snapshots) e o momento (o dump de que você precisa é dos primeiros segundos do incidente, antes de um humano conseguir reagir). Um script de cron não fecha nenhuma das duas direito; o JFR grava continuamente mas deixa o diagnóstico com você; a captura por gatilho — vigiando os sinais de CPU, deadlock, heap e GC que a JVM já expõe — é a abordagem que coloca o dump certo na sua frente com a análise já feita. Comece manual, aprenda a cara da sua linha de base saudável (o guia de captura tem todos os métodos), e automatize quando a repetição mandar.
Perguntas frequentes
Um único thread dump basta para diagnosticar um problema na JVM?
Às vezes — um deadlock é provável a partir de um dump só, porque a própria JVM imprime o ciclo. Mas os problemas que mais doem em produção são tendências: uma contagem de threads que cresce a cada hora, um pool que satura sob carga, um lock que esquenta conforme o tráfego sobe. Esses só aparecem quando você compara snapshots ao longo do tempo — que é exatamente o que monitoramento significa e que um dump sozinho não dá.
Tirar thread dumps prejudica a performance em produção?
Cada dump pausa a JVM brevemente num safepoint — tipicamente alguns milissegundos para uma aplicação com centenas de threads. Tirados a cada poucos segundos durante um incidente, ou por gatilho de threshold com cooldown, o overhead é desprezível. O que evitar é dumpar em loop apertado sem intervalo; qualquer agenda razoável (ou um agente por gatilho com cooldowns) mantém o custo invisível.
Quais gatilhos devem disparar uma captura automática de thread dump?
Os quatro que mapeiam para classes reais de incidente: CPU alta sustentada do processo (não um pico isolado — várias leituras consecutivas), deadlock detectado (ThreadMXBean.findDeadlockedThreads é autoritativo e custa quase nada verificar), heap cravado perto do máximo, e pausas de GC anormalmente longas. Cada gatilho precisa de cooldown para que uma condição persistente produza um punhado de dumps, não milhares.
Preciso de plano pago para monitorar thread dumps com o ThreadMine?
A análise manual é gratuita: o primeiro dump não pede cadastro, e o plano Free dá análises diárias com histórico. O agente de captura — dumps automáticos por gatilho enviados das suas JVMs — faz parte do plano Pro, a partir de US$ 9/mês.
Quer ver tudo isso aplicado ao seu dump?
Faça upload e tenha health score, problemas detectados e sugestões em segundos. Sem cadastro para o primeiro dump.