«1. Обзор

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

2. Утилиты мониторинга Java

Java — это не просто язык программирования, а очень богатая экосистема с большим количеством инструментов. JDK содержит программы, позволяющие компилировать собственные программы, а также отслеживать их состояние и состояние виртуальной машины Java в течение всего жизненного цикла выполнения программы.

Папка bin дистрибутива JDK содержит среди прочего следующие программы, которые можно использовать для профилирования и мониторинга:

    Java VisualVM (jvisualvm.exe) JConsole (jconsole.exe) Java Mission Control (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 (JVM) во время выполнения приложения Java. JFR является частью дистрибутива JDK и интегрирован в JVM.

JFR спроектирован таким образом, чтобы как можно меньше влиять на производительность работающего приложения.

Чтобы использовать JFR, мы должны его активировать. Мы можем добиться этого двумя способами:

  1. when starting a Java application
  2. passing diagnostic commands of the jcmd tool when a Java application is already running

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.

Как мы знаем, дисковые операции ввода-вывода довольно затратны. Поэтому 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

где path-to-class-file — это точка входа приложения *.class файл.

Эта команда запускает приложение и активирует запись, которая начинается сразу и длится не более 200 секунд. Собранные данные сохраняются в выходной файл Flight.jfr. Мы опишем другие варианты более подробно в следующем разделе.

4.2. Инструмент диагностических команд

Мы также можем начать регистрацию событий с помощью инструмента jcmd. Например:

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

До JDK 11, чтобы иметь возможность активировать JFR таким образом, мы должны запускать приложение с разблокированными коммерческими функциями:

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

После запуска приложения мы используем его идентификатор процесса для выполнения различных команд, которые имеют следующий формат:

jcmd <pid|MainClass> <command> [parameters]

Вот полный список диагностических команд:

    JFR.start — запускает новую запись JFR JFR.check — “ проверяет выполняемые записи JFR JFR.stop – останавливает конкретную запись JFR JFR.dump – копирует содержимое записи JFR в файл

Каждая команда имеет ряд параметров. Например, команда JFR.start имеет следующие параметры:

    name — имя записи; служит для того, чтобы иметь возможность ссылаться на эту запись позже с помощью других команд. delay – размерный параметр для временной задержки начала записи, значение по умолчанию равно 0 с. duration – размерный параметр для временного интервала продолжительности записи; значение по умолчанию — 0s, что означает неограниченное имя файла — имя файла, содержащего собранные данные. maxage — размерный параметр для максимального возраста собранных данных; значение по умолчанию — 0s, что означает неограниченный maxsize — максимальный размер буферов для собираемых данных в байтах; значение по умолчанию равно 0, что означает отсутствие максимального размера

Мы уже видели пример использования этих параметров в начале этого раздела. Полный список параметров можно найти в официальной документации Java Flight Recorded.

Хотя JFR спроектирован так, чтобы как можно меньше влиять на производительность JVM и приложения, лучше ограничить максимальный объем собираемых данных, установив хотя бы один из параметров: продолжительность, максимальное значение или максимальный размер.

5. Java Flight Recorder в действии

Давайте теперь продемонстрируем JFR в действии на примере программы.

5.1. Пример программы

Наша программа вставляет объекты в список до тех пор, пока не произойдет ошибка OutOfMemoryError. Затем программа засыпает на одну секунду:

public static void main(String[] args) {
    List<Object> 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());
    }
}

«

«Не выполняя этот код, мы можем заметить потенциальный недостаток: цикл while приведет к высокой загрузке ЦП и памяти. Давайте воспользуемся JFR, чтобы увидеть эти недостатки и, возможно, найти другие.

5.2. Начало регистрации

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

Сначала мы скомпилируем нашу программу, выполнив следующую команду из командной строки:

На этом этапе мы должны найти файл FlightRecorder.class в каталоге out/com/baeldung/flightrecorder.

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

Теперь запустим программу со следующими опциями:

5.3. Визуализация данных

Теперь мы передаем файл Flight.jfr в Java Mission Control, который является частью дистрибутива JDK. Это помогает нам визуализировать данные о наших событиях красивым и интуитивно понятным способом.

Его главный экран показывает нам информацию о том, как программа использовала процессор во время своего выполнения. Мы видим, что процессор был сильно загружен, что вполне ожидаемо из-за цикла while:

В левой части представления мы видим среди прочих разделы General, Memory, Code и Threads. Каждый раздел содержит различные вкладки с подробной информацией. Например, вкладка Hot Methods раздела Code содержит статистику вызовов методов:

В этой вкладке мы можем заметить еще один недостаток нашей программы-примера: метод java.util.ArrayList.grow(int) вызывался 17 раз за чтобы увеличивать емкость массива каждый раз, когда не хватило места для добавления объекта.

    В более реалистичных программах мы можем увидеть много другой полезной информации:

статистика о созданных объектах, когда они были созданы и уничтожены сборщиком мусора подробный отчет о хронологии потоков, когда они были заблокированы или какие операции ввода-вывода выполнялись приложением

6. Заключение

В этой статье мы рассмотрели тему мониторинга и профилирования приложения Java с помощью Java Flight Recorder. Этот инструмент остается экспериментальным, поэтому мы должны посетить его официальный сайт для получения более полной и актуальной информации.