Monitorando aplicativos Java com gravador de vôo
1. Visão geral
Neste tutorial, examinaremos o Java Flight Recorder, seus conceitos, seus comandos básicos e como usá-lo.
2. Utilitários de monitoramento Java
Java não é apenas uma linguagem de programação, mas um ecossistema muito rico com muitas ferramentas. O JDK contém programas que nos permitem compilar nossos próprios programas, bem como monitorar seu estado e o estado da Java Virtual Machine durante todo o ciclo de vida da execução do programa.
A pastabin de uma distribuição JDK contém, entre outros, os seguintes programas que podem ser usados para criação de perfil e monitoramento:
-
Java VisualVM (jvisualvm.exe)
-
JConsole (jconsole.exe)
-
Java Mission Control (jmc.exe)
-
Diagnostic Command Tool (jcmd.exe)
Sugerimos que você explore o conteúdo desta pasta para saber quais ferramentas temos à nossa disposição.
Neste tutorial, vamos nos concentrar no Java Flight Recorder. Isso não está presente entre as ferramentas mencionadas acima porque não é um programa independente. Seu uso está intimamente relacionado a duas das ferramentas acima - Java Mission Control e Diagnostic Command Tools.
3. Gravador de vôo Java e seus conceitos básicos
Java Flight Recorder (JFR) é uma ferramenta de monitoramento quecollects information about the events in a Java Virtual Machine (JVM) during the execution of a Java application. JFR faz parte da distribuição JDK e é integrado ao JVM.
JFR édesigned to affect the performance of a running application as little as possible.
Para usar o JFR, devemos ativá-lo. Podemos conseguir isso de duas maneiras:
-
ao iniciar um aplicativo Java
-
passar comandos de diagnóstico da ferramentathe jcmd quando um aplicativo Java já estiver em execução
JFR não tem uma ferramenta independente. Usamos o Java Mission Control (JMC), que contém um plug-in que nos permite visualizar os dados coletados pelo JFR.
Esses três componentes -JFR,jcmdeJMC - formam um conjunto completo para coletar informações de tempo de execução de baixo nível de um programa Java em execução. Podemos achar essas informações muito úteis ao otimizar nosso programa ou ao diagnosticá-lo quando algo der errado.
Se tivermos várias versões do Java instaladas em nosso computador, é importantemake sure that the Java compiler (javac), the Java launcher (java) and the above-mentioned tools (JFR, jcmd and JMC) are from the same Java distribution. Caso contrário, há o risco de não ser possível ver dados úteis porque os formatos de dados JFR de versões diferentes podem não ser compatíveis.
JFR has two main concepts: events and dataflow. Vamos discuti-los brevemente.
3.1. Eventos
O JFR coleta eventos que ocorrem na JVM quando o aplicativo Java é executado. Esses eventos estão relacionados ao estado da própria JVM ou ao estado do programa. Um evento tem um nome, um carimbo de data e hora e informações adicionais (como informações de encadeamento, pilha de execução e estado do heap).
Existemthree types of events que JFR coleta:
-
an instant event é registrado imediatamente assim que ocorre
-
a duration event é registrado se sua duração for bem-sucedida em um limite especificado
-
a sample event é usado para amostrar a atividade do sistema
3.2. Dataflow
Os eventos que o JFR coleta contêm uma enorme quantidade de dados. Por esse motivo, por design, o JFR é rápido o suficiente para não impedir o programa.
JFR salva dados sobre os eventos em um único arquivo de saída,flight.jfr.
Como sabemos, as operações de E / S do disco são bastante caras. Portanto, o JFR usa vários buffers para armazenar os dados coletados antes de liberar os blocos de dados no disco. As coisas podem se tornar um pouco mais complexas porque, ao mesmo tempo, um programa pode ter vários processos de registro com opções diferentes.
Por causa disso,we may find more data in the output file than requested, or it may not be in chronological order. Podemos nem notar esse fato se usarmos o JMC, porque ele visualiza os eventos em ordem cronológica.
In some rare cases, JFR might fail to flush the data (por exemplo, quando há muitos eventos ou em caso de falta de energia). Se isso ocorrer, o JFR tentará nos informar que o arquivo de saída pode estar com alguns dados ausentes.
4. Como usar o gravador de vôo Java
JFR é um recurso experimental, portanto, seu uso está sujeito a alterações. De fato, em distribuições anteriores, precisamos ativar os recursos comerciais para usá-los na produção. No entanto, a partir do JDK 11, podemos usá-lo sem ativar nada. Sempre podemos consultar as notas de versão oficiais do Java para verificar como usar essa ferramenta.
Para o JDK 8, para poder ativar o JFR, devemos iniciar o JVM com as opções+UnlockCommercialFeaturese+FlightRecorder.
Como mencionamos acima, existem duas maneiras de ativar o JFR. Quando o ativamos simultaneamente ao iniciar o aplicativo, fazemos isso na linha de comando. Quando o aplicativo já está em execução, usamos a ferramenta de comando de diagnóstico.
4.1. Linha de comando
Primeiro, compilamos o arquivo*.java do programa em um*.class usando o compilador java padrãojavac.
Quando a compilação for bem-sucedida, podemos iniciar o programa com as seguintes opções:
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder
-XX:StartFlightRecording=duration=200s,filename=flight.jfr path-to-class-file
ondepath-to-class-file é o arquivo*.class do ponto de entrada do aplicativo.
Este comando inicia o aplicativo e ativa a gravação, que inicia imediatamente e não dura mais de 200 segundos. Os dados coletados são salvos em um arquivo de saídaflight.jfr. Descreveremos as outras opções com mais detalhes na próxima seção.
4.2. Ferramenta de comando de diagnóstico
Também podemos começar a registrar os eventos usando a ferramentajcmd. Por exemplo:
jcmd 1234 JFR.start duration=100s filename=flight.jfr
Antes do JDK 11, para poder ativar o JFR dessa maneira, devemos iniciar o aplicativo com recursos comerciais desbloqueados:
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -cp ./out/ com.example.Main
Depois que o aplicativo está em execução, usamos sua identificação de processo para executar vários comandos, que assumem o seguinte formato:
jcmd [parameters]
Aqui está uma lista completa dos comandos de diagnóstico:
-
JFR.start - inicia uma nova gravação JFR
-
JFR.check - verifica a execução de gravação (ões) JFR
-
JFR.stop - interrompe uma gravação JFR específica
-
JFR.dump - copia o conteúdo de uma gravação JFR para um arquivo
Cada comando possui uma série de parâmetros. Por exemplo, o comandoJFR.start tem os seguintes parâmetros:
-
name - nome da gravação; serve para poder fazer referência a esta gravação mais tarde com outros comandos
-
delay - parâmetro dimensional para um atraso de tempo de início da gravação, o valor padrão é 0s
-
duration - parâmetro dimensional para um intervalo de tempo da duração da gravação; o valor padrão é 0s, o que significa ilimitado
-
filename - nome de um arquivo que contém os dados coletados
-
maxage - parâmetro dimensional para idade máxima dos dados coletados; o valor padrão é 0s, o que significa ilimitado
-
maxsize - tamanho máximo dos buffers para dados coletados em bytes; o valor padrão é 0, o que significa que não há tamanho máximo
Já vimos um exemplo do uso desses parâmetros no início desta seção. Para a lista completa dos parâmetros, podemos sempre consultar oJava Flight Recorded official documentation.
Embora o JFR seja projetado para ter o mínimo de pegada possível no desempenho da JVM e do aplicativo, é melhor limitar a quantidade máxima de dados coletados definindo pelo menos um dos parâmetros:duration,maxage oumaxsize.
5. Gravador de vôo Java em ação
Vamos agora demonstrar o JFR em ação usando um programa de exemplo.
5.1. Programa de exemplo
Nosso programa insere objetos em uma lista até que ocorra umOutOfMemoryError. Então o programa dorme por um segundo:
public static void main(String[] args) {
List
Sem executar esse código, podemos detectar uma desvantagem potencial: o loopwhile levará a um alto uso de CPU e memória. Vamos usar o JFR para ver essas desvantagens e provavelmente encontrar outras.
5.2. Iniciar o registro
Primeiro, compilamos nosso programa executando o seguinte comando na linha de comando:
javac -d out -sourcepath src/main src/main/com/example/flightrecorder/FlightRecorder.java
Neste ponto, devemos encontrar um arquivoFlightRecorder.class no diretórioout/com/example/flightrecorder.
Agora, vamos iniciar o programa com as seguintes opções:
java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder
-XX:StartFlightRecording=duration=200s,filename=flight.jfr
-cp ./out/ com.example.flightrecorder.FlightRecorder
5.3. Visualizar dados
Agora, alimentamos o arquivoflight.jfr paraJava Mission Control, que faz parte da distribuição JDK. Isso nos ajuda a visualizar os dados sobre nossos eventos de maneira agradável e intuitiva.
Sua tela principal mostra as informações sobre como o programa estava usando a CPU durante sua execução. Vemos que a CPU estava muito carregada, o que é bastante esperado devido ao loopwhile:
No lado esquerdo da visualização, vemos as seçõesGeneral,Memory,Code eThreads, entre outras. Cada seção contém várias guias com informações detalhadas. Por exemplo, a guiaHot Methods da seçãoCode contém as estatísticas de chamadas de método:
Nesta guia, podemos identificar outra desvantagem do nosso programa de exemplo: o métodojava.util.ArrayList.grow(int) foi chamado 17 vezes para aumentar a capacidade do array sempre que não havia espaço suficiente para adicionar um objeto.
Em programas mais realistas, podemos ver muitas outras informações úteis:
-
estatísticas sobre objetos criados, quando eles foram criados e destruídos pelo coletor de lixo
-
um relatório detalhado sobre a cronologia dos threads, quando estavam bloqueados ou ativos
-
quais operações de E / S o aplicativo estava executando
6. Conclusão
Neste artigo, apresentamos o tópico de monitoramento e criação de perfil de um aplicativo Java usando o Java Flight Recorder. Esta ferramenta continua a ser experimental, pelo que devemos consultar o seuofficial site para informações mais completas e recentes.
Como sempre, o snippet de código está disponível no repositórioover on our Github.