Participe da Maratona Behind the Code! A competição de programação que mais te desafia! Inscreva-se aqui

Cinco coisas que você não sabia sobre … monitoramento de desempenho Java, Parte 2

Gerenciadores de perfis completos e integrados como JConsole e VisualVM às vezes trazem mais sobrecarga de desempenho do que vantagens — especialmente em sistemas executados no hardware de produção. Por isso, neste segundo artigo que enfoca o monitoramento de desempenho Java, apresentarei cinco ferramentas de gerenciamento de perfis da linha de comando que permitem aos desenvolvedores concentrarem-se apenas em um aspecto de um processo Java em execução.

O JDK inclui diversos utilitários de linha de comando que podem ser usados para monitorar e gerenciar o desempenho do aplicativo Java. Embora vários deles sejam rotulados de “experimentais” e, por isso, tecnicamente não sejam suportados, ainda assim eles são úteis. Alguns deles podem ser até mesmo um material inicial para ferramentas de fins especiais que podem ser desenvolvidas usando JVMTI ou JDI.

1. jps (sun.tools.jps)

Diversas ferramentas de linha de comando exigem que você identifique o processo Java que deseja monitorar. Isso não é muito diferente de ferramentas semelhantes que monitoram processos do sistema operacional nativo e também requerem o funcionamento de um identificador de processos.

O identificador “VMID” nem sempre é igual ao identificador de processos (“pid”) do sistema operacional nativo, motivo pelo qual precisamos do utilitário jps do JDK.

jps — cujo nome reflete o utilitário ps localizado na maior parte dos sistemas UNIX — nos informa o JVMID do aplicativo Java em execução. Como seu nome implica, o jps retorna os VMIDs para todos os processos Java em execução detectáveis em uma determinada máquina. Se o jps não detectar um processo, isso não significa que o processo Java não possa anexado ou explorado. Significa apenas que ele não está anunciando a si mesmo como disponível.

Se for possível localizar o processo Java, o jps irá listar a linha de comando usada para ativá-lo. Essa forma de diferenciação entre processos Java é importante porque, no que se refere ao sistema operacional, todos os programas Java são “java.” Para a maioria dos propósitos, o VMID é um número importante a ser observado.

Introdução aos gerenciadores de perfis

A forma mais fácil de introdução aos utilitários de gerenciamento de perfis é usar um programa demo como o SwingSet2, disponível em demo/jfc/SwingSet2. Isso evita possíveis desligamentos com os processos sendo executados como processos em segundo plano/processos daemon. Quando você estiver familiarizado com a ferramenta e sua sobrecarga, poderá testá-la em seus programas reais.

Depois que você carregar seu aplicativo demo, execute o jps e anote o retorno do vmid. Para obter melhores efeitos, inicie o programa Java com o conjunto de propriedades -Dcom.sun.management.jmxremote. Se você não usar essa configuração, alguns dados reunidos por algumas das ferramentas a seguir não estarão disponíveis.

2. jstat (sun.tools.jstat)

O utilitário jstat pode ser usado para reunir diversas estatísticas diferentes. As estatísticas jstat são classificadas em “opções” que são especificadas na linha de comando como primeiro parâmetro. A partir do JDK 1.6, é possível exibir a lista de opções disponíveis executando jstat com o comando -options. Algumas das opções são mostradas na Listagem 1:

Listagem 1. jstat options

-class
-compiler
-gc
-gccapacity
-gccause
-gcnew
-gcnewcapacity
-gcold
-gcoldcapacity
-gcpermcapacity
-gcutil
-printcompilation

A documentação do JDK do utilitário informa o que cada uma das opções na Listagem 1 retorna, mas a maior parte delas é usada para reunir informações de desempenho sobre o coletor de lixo, ou apenas de partes dele. A opção -class revela classes carregadas e descarregadas (o que a torna um grande utilitário para detectar fugas de ClassLoader dentro do servidor de aplicativos ou em seu código) e tanto -compiler como -printcompilation exibem informações sobre o compilador Hotspot JIT.

Por padrão, jstat exibirá as informações no momento em que você verificá-las. Se você desejar fazer capturas instantâneas em intervalos regulares, especifique os intervalos em milissegundos seguindo o comando -options. jstat exibirá continuamente capturas instantâneas das informações do processo monitorado. Se você desejar que o jstat realize um número específico de capturas instantâneas antes de encerrar, especifique esse número após o valor de intervalo/período.

Se 5756 fosse o VMID de um processo SwingSet2 em execução iniciado há alguns minutos, o seguinte comando informaria a jstat para produzir um dump de captura instantânea gc a cada 250 milissegundos para 10 iterações e, em seguida, seria encerrado:

jstat -gc 5756 250 10

Observe que a Sun (agora Oracle) reservava-se o direito de alterar a saída das várias opções, ou até mesmo as opções em si, sem aviso. Esse é o aspecto negativo de usar utilitários não suportados. Consulte os Javadocs para obter detalhes completos sobre cada uma das colunas na saída de jstat.

3. jstack (sun.tools.jstack)

Saber o que está acontecendo dentro de um processo Java em comparação ao aos encadeamentos de execução é um desafio de diagnóstico comum. Por exemplo, quando um aplicativo para de processar repentinamente, é óbvio que algum tipo de inanição foi atingido, mas, apenas observando o código, não é tão simples saber onde ocorreu a inanição, nem por quê.

jstack é um utilitário que retorna um dump completo dos vários encadeamentos em execução em um aplicativo, e que pode ser usado para apontar o problema.

A execução de jstack com o VMID do processo desejado irá gerar um dump de pilha. Nesse caso, jstack funciona da mesma forma como pressionar Ctrl-Break na janela do console no qual um programa Java está sendo executado ou chamar Thread.getAllStackTraces() ou Thread.dumpStack() em cada um dos objetos Thread na MV. Uma chamada jstack também executa dump de informações sobre encadeamentos não Java em execução na MV, que nem sempre estão disponíveis como objetos Thread.

jstack‘s -l é um parâmetro que oferece um dump ligeiramente mais longo que inclui informações mais detalhadas sobre os bloqueios existentes em cada um dos encadeamentos Java e, assim, pode ter valor inestimável na localização (e compressão!) de conflitos ou erros de escalabilidade.

4. jmap (sun.tools.jmap)

Às vezes, o problema que você está enfrentando é uma fuga de objeto, como uma ArrayList (que pode conter milhares de objetos) que simplesmente não está sendo liberada quando deveria. Outro problema mais geral seria um heap em expansão que parece nunca se reduzir, apesar da coleta de lixo ativa.

Quando você estiver tentando localizar uma fuga de objeto, é incrivelmente útil fazer uma captura do heap em um determinado momento e, em seguida, observar atentamente o que há lá. jmap fornece a primeira parte dessa funcionalidade realizando uma captura instantânea do heap. Em seguida, é possível analisar os dados do heap usando o utilitário jhat descrito na próxima seção.

O uso do jmap é simples, como todos os outros utilitários descritos aqui. Basta apontar o jmap para o VMID do processo Java que você deseja capturar e fornecer alguns parâmetros para descrever o arquivo resultante produzido. As opções que você irá passar para jmap consistem no nome do arquivo no qual executar dump e na escolha do uso de um arquivo de texto ou de um arquivo binário. Um arquivo binário é a opção mais útil, mas somente quando combinado com algum tipo de ferramenta de indexação — trabalhar manualmente em vários megabytes de texto cheio de valores hexadecimais não é a melhor forma de passar seu dia.

Para uma análise mais casual do heap Java, o jmap também suporta uma opção -histo. -histo produz um histograma de texto dos objetos que, no momento, estão fortemente referenciados no heap, classificados pelo número total de bytes consumidos por esse tipo específico. Ele também fornece o número total de instâncias desse tipo particular, o que permite alguns cálculos simples e suposições sobre o custo relativo por instância.

Infelizmente, o jmap não tem uma opção de período e contagem máxima como tem o jstat, mas é relativamente fácil inserir chamadas para jmap (ou para jmap.main()) no loop de um shell script ou em outra classe para realizar capturas instantâneas periodicamente. (De fato, essa seria uma boa extensão a ser adicionada ao jmap, como uma correção de origem para o próprio OpenJDK ou como uma extensão para outro utilitário.)

5. jhat (com.sun.tools.hat.Main)

Depois que você executar dump no heap em um arquivo binário, poderá usar o jhat para analisar o arquivo binário dump do heap. jhat cria um servidor HTTP/HTML que pode ser explorado em um navegador, fornecendo uma visualização objeto por objeto do heap, congelado no tempo. Embora possa ser divertido analisar o heap, referência do objeto por referência do objeto, provavelmente será mais vantajoso fazer algum tipo de análise automatizada de tudo isso. Felizmente, o jhat suporta uma sintaxe OQL para a realização dessa análise.

Por exemplo, a execução de uma consulta OQL para toda String com mais de 100 caracteres teria a seguinte aparência:

select s from java.lang.String s where s.count >= 100

Os resultados são exibidos como links para os objetos que, em seguida, exibem o conteúdo completo desse objeto e as referências de campo para outros objetos e também como links adicionais que podem ser desreferenciados. As consultas OQL também podem chamar métodos nos objetos em si, usar expressões regulares como parte da consulta e também ferramentas de consulta integradas. Uma ferramenta de consulta, a função referrers(), exibe todos os referentes que referenciam um objeto de um determinado tipo. Esta é a consulta para localizar todos os referentes para objetos File:

select referrers(f) from java.io.File f

É possível encontrar a sintaxe completa de OQL e seus recursos na página “OQL Help” no ambiente do navegador jhat. A combinação de jhat com OQL é uma poderosa maneira de realizar uma investigação orientada de um heap com comportamento incorreto.

Conclusão

As extensões de gerenciamento de perfis do JDK podem ser muito úteis quando for necessário obter uma visão mais detalhada do que está acontecendo dentro de um processo Java. Todas as ferramentas apresentadas neste artigo podem ser usadas de forma autônoma, a partir da linha de comando. Elas também podem ser combinadas com o JConsole ou o VisualVM. Considerando que o JConsole e o VisualVM fornecem uma visualização abrangente da máquina virtual Java, ferramentas especificamente focadas como jstat e jmap permitem ajustar sua investigação.

Aviso

O conteúdo aqui presente foi traduzido da página IBM Developer US. Caso haja qualquer divergência de texto e/ou versões, consulte o conteúdo original.