Conteúdo


Cinco coisas que você não sabia sobre ... os sinalizadores de linha de comando para JVM

Ajuste com exatidão o desempenho da JVM e o Java runtime

Comments

A JVM é que faz o trabalho duro por trás da funcionalidade e do desempenho do aplicativo Java que a maioria dos desenvolvedores Java considera natural. E ainda assim pouquíssimos profissionais na área realmente entendem como a JVM faz o que ela faz — coisas como alocar e definir objetos de coleta de lixo, prolongar encadeamentos, abrir e fechar arquivos, interpretar e/ou executar compilação JIT para bytecode Java e mais.

A falta de familiaridade com a JVM não somente ocasiona perdas em termos de desempenho do aplicativo, como também quando algo vai mal com a JVM, pode ser muito difícil corrigir.

Este capítulo da série cinco coisas apresenta um útil sinalizador de JVM de linha de comando que você pode usar para diagnosticar e ajustar o desempenho de sua Java virtual machine.

1. DisableExplicitGC

Eu perdi a conta de quantas vezes fui solicitado a realizar uma consultoria sobre um problema de desempenho de aplicativo, de quantas vezes executei um grep rápido no código e descobri o que é mostrado na Listagem 1 — o antipadrão de desempenho Java original:

Listagem 1. System.gc();
// We just released a bunch of objects, so tell the stupid
// garbage collector to collect them already!
System.gc();

A coleta de lixo explícita é uma ideia realmente ruim,— algo como estar preso em uma cabine telefônica com um pit bull raivoso. Embora a semântica exata da chamada dependa da implementação, supondo-se que a JVM esteja executando um coletor de lixo de geração (o que é a maioria deles), System.gc(); força a VM a realizar uma "varredura completa" do heap, mesmo que não seja necessário. As varreduras completas são, de modo geral, muitas vezes mais caras do que uma operação de GC regular, que é apenas matemática simples ineficiente.

Mas não acredite muito nisso — os engenheiros da Sun nos forneceram um sinalizador de JVM somente para este problema de erro humano em particular: o sinalizador -XX:+DisableExplicitGC automaticamente transforma uma chamada de System.gc() em uma no-op, oferecendo-lhe a oportunidade de executar seu código e você mesmo constatar se o System.gc() ajudou ou prejudicou a execução geral da JVM.

2. HeapDumpOnOutOfMemoryError

Você já passou por situações em que a JVM apresentou problemas, emitindo mensagens do tipo OutOfMemoryError, e não conseguiu configurar o depurador para capturar o erro e verificar qual era o problema? Problemas esporádicos e/ou sem possibilidade de determinação como esse podem deixar um desenvolvedor completamente maluco.

O que você deseja em momentos como esse é obter uma captura instantânea do heap exatamente quando a JVM estiver entrando em colapso — e isso é exatamente o que o comando -XX:+HeapDumpOnOutOfMemoryError faz.

A execução desse comando diz à JVM para obter uma "captura instantânea do dump do heap" e salvá-lo em um arquivo para processamento, geralmente usando o utilitário jhat (que eu apresentei em um artigo anterior). É possível especificar o caminho real no qual o arquivo é salvo usando o sinalizador -XX:HeapDumpPath correspondente. (Independentemente de onde o arquivo for salvo, certifique-se de que o sistema de arquivos e/ou o processo Java tenha a configuração de permissão necessária para poder gravá-lo.)

3. bootclasspath

Periodicamente, é útil enviar uma classe para o caminho de classe que seja um pouco diferente do que acompanha o JRE comum ou do que de certa forma estende o JRE. (Um exemplo seria um novo provedor Java Crypto API). Se você quiser estender o JRE, será necessário que sua implementação customizada esteja disponível para o ClassLoader de autoinicialização, que carrega o java.lang.Object e todos os seus colegas no rt.jar.

Embora você pudesse abrir o rt.jar e inserir nele sua implementação customizada ou novo pacote, isso tecnicamente violaria a licença com a qual concordou quando transferiu o JDK por download.

Em vez disso, use a opção -Xbootclasspath da própria JVM, juntamente com -Xbootclasspath/p e -Xbootclasspath/a.

-Xbootclasspath permite que você configure o caminho de classe de boot completo, que geralmente tem que incluir uma referência para rt.jar, além de vários outros arquivos JAR que acompanham o JDK que não fazem parte do rt.jar. -Xbootclasspath/p acrescenta no início o valor do bootclasspath existente, e -Xbootclasspath/a o anexa no final.

Se, por exemplo, você tiver modificado o java.lang.Integercomum e colocar as modificações em um subdiretório, mods, um parâmetro -Xbootclasspath/a mods colocará o novo número inteiro na frente do valor padrão.

4. verbose

-verbose é um importante utilitário de diagnóstico para praticamente qualquer tipo de aplicativo Java. O sinalizador possui três subsinalizadores:gc, class e jni.

gc geralmente é o primeiro lugar que os desenvolvedores verificam para tentar descobrir se o coletor de lixo da JVM está falhando e provocando o desempenho insatisfatório. Infelizmente, interpretar a saída de gc pode ser complicado — tanto é que isso tem sido assunto de livros inteiros. E ainda pior, a saída impressa da linha de comando pode mudar de um release Java para outro ou de uma JVM para outra, dificultando ainda mais a interpretação correta.

Falando de modo geral, se o coletor de lixo for um coletor de geração (o que a maioria das VMs de "classe corporativa" é), algum tipo de sinalizador visível aparecerá para indicar a passagem do GC de varredura completa; na JVM Sun, o sinalizador aparece como "[Full GC ...]" no início da linha de saída do GC.

class pode ser uma solução para tentar diagnosticar conflitos de ClassLoader e/ou de classe incompatível. Ele relata não somente quando uma classe é carregada, mas também de onde a classe foi carregada, incluindo o caminho para o arquivo JAR, supondo que ela venha de um JAR.

jni é de pouco uso, exceto ao se trabalhar com bibliotecas nativas e JNI. Quando ativado, ele relatará vários eventos JNI, como quando as bibliotecas nativas são carregadas e quando os métodos são ligados; novamente, a saída pode variar de um release ou JVM para outro.

5. -X de linha de comando

Eu listei algumas de minhas opções de linha de comando favoritas que a JVM oferece, mas há tantas outras que você mesmo poderá descobrir. A execução do argumento de linha de comando -X lista todos os argumentos não padrão (mas muito seguros) que a JVM oferece— coisas como:

  • -Xint, que executa a JVM no modo interpretado (que pode ser útil para testar se o compilador JIT está realmente afetando seu código ou verificando se o compilador JIT tem algum erro).
  • -Xloggc:, que faz a mesma coisa que -verbose:gc mas registra em um arquivo em vez de emitir na janela de linha de comando.

As opções da linha de comando JVM são alteradas periodicamente, portanto, verificá-las ocasionalmente é uma boa ideia. Isso pode representar a diferença entre passar a noite em frente ao monitor ou ir para casa às 17h, jantar com a esposa e filhos (ou massacrar seus inimigos no Mass Effect 2, dependendo de sua preferência).

Conclusão

Os sinalizadores de linha de comando não são indicados para uso permanente em um ambiente de produção — na verdade, com exceção dos sinalizadores que você (pode) acabar usando para ajustar o coletor de lixo da JVM, nenhum sinalizador de linha de comando não padrão é realmente indicado para uso na produção. Mas como ferramentas para examinar os trabalhos internos da máquina virtual completamente obscura eles são de grande valia.

A seguir, na série cinco coisas: Ferramentas Java de todos os dias.


Recursos para download


Temas relacionados


Comentários

Acesse ou registre-se para adicionar e acompanhar os comentários.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Software livre
ArticleID=525197
ArticleTitle=Cinco coisas que você não sabia sobre ... os sinalizadores de linha de comando para JVM
publish-date=08242010