Мониторинг на Java приложения с Flight Recorder

1. Общ преглед

В този урок ще разгледаме Java Flight Recorder, неговите концепции, основните команди и как да го използваме.

2. Помощни програми за наблюдение на Java

Java не е просто език за програмиране, а много богата екосистема с много инструменти. JDK съдържа програми, които ни позволяват да компилираме собствените си програми, както и да наблюдаваме тяхното състояние и състоянието на виртуалната машина Java по време на пълния жизнен цикъл на изпълнение на програмата.

В бин папката на разпределение JDK съдържа, наред с другото, следните програми, които могат да се използват за профилиране и мониторинг:

  • Java VisualVM (jvisualvm.exe)
  • JConsole (jconsole.exe)
  • Контрол на мисията на Java (jmc.exe)
  • Диагностичен команден инструмент (jcmd.exe)

Предлагаме да проучите съдържанието на тази папка, за да сте наясно с какви инструменти разполагаме. Моля, обърнете внимание, че Java VisualVM е била част от дистрибуциите Oracle и Open JDK в миналото. Въпреки това, започвайки от Java 9, JDK дистрибуциите вече не се доставят с Java VisualVM . Затова трябва да го изтеглим отделно от уебсайта на проекта с отворен код VisualVM.

В този урок ще се съсредоточим върху Java Flight Recorder. Това не е сред инструментите, споменати по-горе, защото не е самостоятелна програма. Използването му е тясно свързано с два от инструментите по-горе - Java Mission Control и Diagnostic Command Tools.

3. Java Flight Recorder и основните му понятия

Java Flight Recorder (JFR) е инструмент за наблюдение, който събира информация за събитията в Java Virtual Machine (JVM) по време на изпълнението на Java приложение . JFR е част от JDK дистрибуцията и е интегрирана в JVM.

JFR е проектиран да повлияе на производителността на работещо приложение възможно най-малко .

За да използваме JFR, трябва да го активираме. Можем да постигнем това по два начина:

  1. при стартиране на Java приложение
  2. предаване на диагностични команди на инструмента jcmd , когато Java приложение вече работи

JFR няма самостоятелен инструмент. Използваме Java Mission Control (JMC), който съдържа плъгин, който ни позволява да визуализираме данните, събрани от JFR.

Тези три компонента - JFR , jcmd и JMC - формират пълен пакет за събиране на информация за ниско ниво на изпълнение на работеща програма Java. Може да намерим тази информация за много полезна, когато оптимизираме нашата програма или когато я диагностицираме, когато нещо се обърка.

Ако на компютъра ни са инсталирани различни версии на Java, важно е да се уверите, че компилаторът на Java ( javac ), стартера на Java ( java ) и гореспоменатите инструменти (JFR, jcmd и JMC) са от една и съща Java дистрибуция . В противен случай съществува риск да не успеете да видите полезни данни, тъй като форматите на данни JFR от различни версии може да не са съвместими.

JFR има две основни концепции: събития и поток от данни. Нека ги обсъдим накратко.

3.1. Събития

JFR събира събития, които се случват в JVM, когато се изпълнява приложението Java. Тези събития са свързани със състоянието на самия JVM или състоянието на програмата. Събитието има име, клеймо за време и допълнителна информация (като информация за нишки, стек за изпълнение и състояние на купчината).

Има три вида събития, които JFR събира:

  • незабавно събитие се регистрира незабавно, след като се случи
  • събитие за продължителност се регистрира, ако продължителността му надхвърли определен праг
  • примерно събитие се използва за извадка на системната дейност

3.2. Поток от данни

Събитията, които JFR събира, съдържат огромно количество данни. Поради тази причина JFR по дизайн е достатъчно бърз, за ​​да не възпрепятства програмата.

JFR записва данни за събитията в един изходен файл, flight.jfr.

Както знаем, дисковите I / O операции са доста скъпи. Следователно JFR използва различни буфери за съхраняване на събраните данни, преди да изчисти блоковете данни на диск. Нещата може да станат малко по-сложни, защото в един и същи момент програмата може да има множество регистриращи процеси с различни опции.

Поради това може да намерим повече данни в изходния файл, отколкото е поискано, или може да не е в хронологичен ред . Може дори да не забележим този факт, ако използваме JMC, защото той визуализира събитията в хронологичен ред.

В някои редки случаи JFR може да не успее да измие данните (например, когато има твърде много събития или в случай на прекъсване на електрозахранването). Ако това се случи, JFR се опитва да ни информира, че в изходния файл може да липсва част от данните.

4. Как да използвам Java Flight Recorder

JFR е експериментална функция, поради което използването му подлежи на промяна. Всъщност при по-ранни дистрибуции трябва да активираме търговски характеристики, за да ги използваме в производството. Въпреки това, започвайки от JDK 11, можем да го използваме, без да активираме нищо. Винаги можем да се консултираме с официалните бележки за изданието на Java, за да проверим как да използваме този инструмент.

За да можем JDK 8 да активираме JFR, трябва да стартираме JVM с опциите + UnlockCommercialFeatures и + FlightRecorder .

Както споменахме по-горе, има два начина за активиране на JFR. Когато го активираме едновременно със стартиране на приложението, ние го правим от командния ред. Когато приложението вече работи, използваме инструмента за диагностична команда.

4.1. Командна линия

Първо, ние компилираме файла * .java на програмата в * .class, използвайки стандартния java компилатор javac .

След като компилацията успее, можем да стартираме програмата със следните опции:

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

където път-до-клас-файл е входната точка на приложението * .class файл.

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.

Както винаги, кодовият фрагмент е достъпен в нашето хранилище на Github.