Monitoraggio di applicazioni Java con Flight Recorder

1. Panoramica

In questo tutorial, esamineremo Java Flight Recorder, i suoi concetti, i suoi comandi di base e come usarlo.

2. Utilità di monitoraggio Java

Java non è solo un linguaggio di programmazione, ma un ecosistema molto ricco con molti strumenti. Il JDK contiene programmi che ci consentono di compilare i nostri programmi, nonché monitorare il loro stato e lo stato della Java Virtual Machine durante l'intero ciclo di vita dell'esecuzione del programma.

La cartella bin di una distribuzione JDK contiene, tra gli altri, i seguenti programmi che possono essere utilizzati per la profilazione e il monitoraggio:

  • Java VisualVM (jvisualvm.exe)
  • JConsole (jconsole.exe)
  • Java Mission Control (jmc.exe)
  • Strumento di comando diagnostico (jcmd.exe)

Suggeriamo di esplorare il contenuto di questa cartella per essere consapevoli di quali strumenti abbiamo a nostra disposizione. Si noti che Java VisualVM faceva parte delle distribuzioni Oracle e Open JDK in passato. Tuttavia, a partire da Java 9, le distribuzioni JDK non vengono più fornite con Java VisualVM . Pertanto, dovremmo scaricarlo separatamente dal sito Web del progetto open source VisualVM.

In questo tutorial, ci concentreremo sul Java Flight Recorder. Questo non è presente tra gli strumenti sopra menzionati perché non è un programma autonomo. Il suo utilizzo è strettamente correlato a due degli strumenti di cui sopra: Java Mission Control e Diagnostic Command Tools.

3. Registratore di volo Java e suoi concetti di base

Java Flight Recorder (JFR) è uno strumento di monitoraggio che raccoglie informazioni sugli eventi in una JVM (Java Virtual Machine) durante l'esecuzione di un'applicazione Java . JFR fa parte della distribuzione JDK ed è integrato nella JVM.

JFR è progettato per influire il meno possibile sulle prestazioni di un'applicazione in esecuzione .

Per poter utilizzare JFR, dovremmo attivarlo. Possiamo raggiungere questo obiettivo in due modi:

  1. all'avvio di un'applicazione Java
  2. passando i comandi diagnostici dello strumento jcmd quando un'applicazione Java è già in esecuzione

JFR non dispone di uno strumento autonomo. Utilizziamo Java Mission Control (JMC), che contiene un plugin che ci permette di visualizzare i dati raccolti da JFR.

Questi tre componenti - JFR , jcmd e JMC - formano una suite completa per la raccolta di informazioni di runtime di basso livello di un programma Java in esecuzione. Potremmo trovare queste informazioni molto utili durante l'ottimizzazione del nostro programma o durante la diagnosi quando qualcosa va storto.

Se abbiamo diverse versioni di Java installate sul nostro computer, è importante assicurarsi che il compilatore Java ( javac ), il launcher Java ( java ) e gli strumenti sopra menzionati (JFR, jcmd e JMC) provengano dalla stessa distribuzione Java . In caso contrario, c'è il rischio di non essere in grado di visualizzare dati utili perché i formati di dati JFR di versioni diverse potrebbero non essere compatibili.

JFR ha due concetti principali: eventi e flusso di dati. Discutiamoli brevemente.

3.1. Eventi

JFR raccoglie gli eventi che si verificano nella JVM quando viene eseguita l'applicazione Java. Questi eventi sono correlati allo stato della JVM stessa o allo stato del programma. Un evento ha un nome, un timestamp e informazioni aggiuntive (come informazioni sul thread, stack di esecuzione e stato dell'heap).

Esistono tre tipi di eventi che JFR raccoglie:

  • un evento istantaneo viene registrato immediatamente una volta che si verifica
  • un evento di durata viene registrato se la sua durata supera una soglia specificata
  • un evento di esempio viene utilizzato per campionare l'attività del sistema

3.2. Flusso di dati

Gli eventi raccolti da JFR contengono un'enorme quantità di dati. Per questo motivo, in base alla progettazione, JFR è abbastanza veloce da non ostacolare il programma.

JFR salva i dati sugli eventi in un unico file di output, flight.jfr.

Come sappiamo, le operazioni di I / O del disco sono piuttosto costose. Pertanto, JFR utilizza vari buffer per memorizzare i dati raccolti prima di svuotare i blocchi di dati su disco. Le cose potrebbero diventare un po 'più complesse perché, allo stesso momento, un programma potrebbe avere più processi di registrazione con diverse opzioni.

Per questo motivo , potremmo trovare più dati nel file di output di quelli richiesti, oppure potrebbe non essere in ordine cronologico . Potremmo anche non notare questo fatto se usiamo JMC, perché visualizza gli eventi in ordine cronologico.

In alcuni rari casi, JFR potrebbe non riuscire a svuotare i dati (ad esempio, quando ci sono troppi eventi o in caso di interruzione di corrente). In tal caso, JFR tenta di informarci che nel file di output potrebbe mancare un dato.

4. Come utilizzare Java Flight Recorder

JFR è una funzionalità sperimentale, quindi il suo utilizzo è soggetto a modifiche. Infatti, nelle distribuzioni precedenti, dobbiamo attivare funzionalità commerciali per poterlo utilizzare in produzione. Tuttavia, a partire da JDK 11, possiamo usarlo senza attivare nulla. Possiamo sempre consultare le note di rilascio ufficiali di Java per verificare come utilizzare questo strumento.

Per JDK 8, per poter attivare JFR, dovremmo avviare la JVM con le opzioni + UnlockCommercialFeatures e + FlightRecorder .

Come accennato in precedenza, ci sono due modi per attivare JFR. Quando lo attiviamo contemporaneamente all'avvio dell'applicazione, lo facciamo dalla riga di comando. Quando l'applicazione è già in esecuzione, utilizziamo lo strumento di comando diagnostico.

4.1. Riga di comando

Per prima cosa, compiliamo il file * .java del programma in un * .class utilizzando il compilatore java standard javac .

Una volta che la compilazione ha successo, possiamo avviare il programma con le seguenti opzioni:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=200s,filename=flight.jfr path-to-class-file

dove path-to-class-file è il file * .class del punto di ingresso dell'applicazione.

This command launches the application and activates the recording, which starts immediately and lasts no more than 200 seconds. Collected data is saved in an output file, flight.jfr. We'll describe the other options in more detail in the next section.

4.2. Diagnostic Command Tool

We can also start registering the events by using the jcmd tool. For example:

jcmd 1234 JFR.start duration=100s filename=flight.jfr

Prior to JDK 11, in order to be able to activate JFR in this way, we should start the application with unlocked commercial features:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -cp ./out/ com.baeldung.Main

Once the application is running, we use its process id in order to execute various commands, which take the following format:

jcmd   [parameters]

Here's a complete list of the diagnostic commands:

  • JFR.start – starts a new JFR recording
  • JFR.check – checks running JFR recording(s)
  • JFR.stop – stops a specific JFR recording
  • JFR.dump – copies contents of a JFR recording to file

Each command has a series of parameters. For example, the JFR.start command has the following parameters:

  • name – the name of the recording; it serves to be able to reference this recording later with other commands
  • delay – dimensional parameter for a time delay of recording start, the default value is 0s
  • duration – dimensional parameter for a time interval of the duration of the recording; the default value is 0s, which means unlimited
  • filename – the name of a file that contains the collected data
  • maxage – dimensional parameter for the maximum age of collected data; the default value is 0s, which means unlimited
  • maxsize – the maximum size of buffers for collected data in bytes; the default value is 0, which means no max size

We've already seen an example of the usage of these parameters at the beginning of this section. For the complete list of the parameters, we may always consult the Java Flight Recorded official documentation.

Although JFR is designed to have as little of a footprint as possible on the performance of the JVM and the application, it's better to limit the maximum amount of collected data by setting at least one of the parameters: duration, maxage, or maxsize.

5. Java Flight Recorder in Action

Let's now demonstrate JFR in action by using an example program.

5.1. Example Program

Our program inserts objects into a list until an OutOfMemoryError occurs. Then the program sleeps for one second:

public static void main(String[] args) { List items = new ArrayList(1); try { while (true){ items.add(new Object()); } } catch (OutOfMemoryError e){ System.out.println(e.getMessage()); } assert items.size() > 0; try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println(e.getMessage()); } }

Without executing this code, we can spot a potential drawback: the while loop will lead to high CPU and memory usage. Let's use JFR to see these drawbacks and probably find others.

5.2. Start Registering

First, we compile our program by executing the following command from the command line:

javac -d out -sourcepath src/main src/main/com/baeldung/flightrecorder/FlightRecorder.java

At this point, we should find a file FlightRecorder.class in the out/com/baeldung/flightrecorder directory.

Now, we'll start the program with the following options:

java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:StartFlightRecording=duration=200s,filename=flight.jfr -cp ./out/ com.baeldung.flightrecorder.FlightRecorder

5.3. Visualize Data

Now, we feed the file flight.jfr to Java Mission Control, which is part of the JDK distribution. It helps us visualize the data about our events in a nice and intuitive way.

Its main screen shows us the information about how the program was using the CPU during its execution. We see that the CPU was loaded heavily, which is quite expected due to the while loop:

On the left side of the view, we see sections General, Memory, Code, and Threads, among others. Each section contains various tabs with detailed information. For example, tab Hot Methods of section Code contains the statistics of method calls:

In this tab, we can spot another drawback of our example program: method java.util.ArrayList.grow(int) has been called 17 times in order to enlarge the array capacity every time there wasn't enough space for adding an object.

In more realistic programs, we may see a lot of other useful information:

  • statistics about created objects, when they were created and destroyed by the garbage collector
  • a detailed report about the threads' chronology, when they were locked or active
  • which I/O operations the application was executing

6. Conclusion

In this article, we introduced the topic of monitoring and profiling a Java application using Java Flight Recorder. This tool remains an experimental one, so we should consult its official site for more complete and recent information.

Come sempre, lo snippet di codice è disponibile sul nostro repository Github.