Рубрики
Без рубрики

Java 11 – Бортовой самописец Java

– Java 11 – Бортовой самописец Java

Java Flight Recorder (JFR) – это инструмент профилирования Java, который используется для мониторинга и диагностики запущенного приложения Java, он собирает данные о запущенной среде, JVM и приложении Java и сбрасывает записанные данные в файл .jfr , и мы можем использовать Java Mission Control (JMC) для анализа и визуализации файла .jfr .

Протестировано с

Краткая история До Java 11 как регистратор полетов Java (JFR), так и Java Mission Control (JMC) являются коммерческими продуктами и доступны только в Oracle JDK, и мы можем включить функции JFR с помощью следующих команд:

$ java -XX:+UnlockCommercialFeatures -XX:+FlightRecorder MyHelloWorldApp

Теперь ПРЕДЛОЖЕНИЕ с открытым исходным кодом в OpenJDK 11 и доступно в папке OpenJDK 11/bin ; хотя JMC больше не является частью JDK, мы можем скачать здесь .

В этой статье мы покажем вам, как использовать Java Flight Recorder (JFR) для мониторинга и сбора данных из Java-приложения с утечкой памяти, сбрасывать данные в файл .jfr и использовать Java Mission Control (JMC) для анализа и визуализации данных, чтобы найти причину утечек памяти.

1. Ошибка памяти (ООМ)

Мы создаем программу-производителя и потребителя Java, которая быстро генерирует утечки памяти и запускает популярный java.lang. Ошибка OutOfMemoryError исключение во время выполнения.

Производитель генерирует объект слишком быстро, и в LinkedBlockingQueue нет ограничений, вызывающих java.lang. OutOfMemoryError это всего лишь вопрос времени.

import java.util.concurrent.*;

public class MemoryLeak {

    // no limit!
    private static BlockingQueue queue = new LinkedBlockingQueue<>();

    public static void main(String[] args) {

        Runnable producer = () -> {
            while (true) {
                // generates 1mb of object every 10ms
                queue.offer(new byte[1 * 1024 * 1024]);
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Runnable consumer = () -> {
            while (true) {
                try {
                    // process every 100ms
                    queue.take();
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        // give a name, good for profiling
        new Thread(producer, "Producer Thread").start();
        new Thread(consumer, "Consumer Thread").start();

    }

}

Выход – Через 10 или 20 секунд…

Exception in thread "Producer Thread" java.lang.OutOfMemoryError: Java heap space
	at com.mkyong.java11.jep328.OOME.lambda$main$0(OOME.java:14)
	at com.mkyong.java11.jep328.OOME$$Lambda$14/0x0000000800066840.run(Unknown Source)
	at java.base/java.lang.Thread.run(Thread.java:834)

2. Бортовой самописец Java (JFR)

2.1 Чтобы включить запись JFR, запустите вышеупомянутое приложение Java с опцией - XX: Запись стартового полета . Приведенный ниже пример запускает 30-секундную запись JFR с фиксированным временем для MemoryLeak.java и сбрасывает записанные данные в файл с именем отсутствие/|.

$ javac MemoryLeak.java

$ java -XX:StartFlightRecording=duration=30s,settings=profile,filename=leak.jfr MemoryLeak

Started recording 1. The result will be written to:

{pwd}/leak.jfr

Exception in thread "Producer Thread" java.lang.OutOfMemoryError: Java heap space
        at MemoryLeak.lambda$main$0(MemoryLeak.java:12)
        at MemoryLeak$$Lambda$71/0x00000008000ff840.run(Unknown Source)
        at java.base/java.lang.Thread.run(Thread.java:834)

Через 10 или 20 секунд он выдаст ожидаемый java.lang. Через 10 или 20 секунд он выдаст ожидаемый java.lang. CTRL+C , чтобы остановить и уничтожить потоки. Позже мы можем использовать Java Mission Control (JMC) для анализа leak.jfr чтобы найти утечки памяти.

2.2 Настройки JFR. Если опущено, значение по умолчанию равно по умолчанию.jfc .

  • по умолчанию.jfc (по умолчанию) – Низкие накладные расходы, хорошо подходит для продолжения записи.
  • profile.jfc – Больше накладных расходов, больше записанных данных и событий, чем по умолчанию.jfc , подходит для кратковременной записи.

В этом примере мы используем настройки JFR профиль , потому что мы знаем MemoryLeak.java выбросит Ошибку памяти очень скоро 🙂

P.S Файлы настроек JFR доступны в OpenJDK 11_путь/библиотека/jfreechart .

Дальнейшее чтение – Обратитесь к этой ссылке Параметры JFR .

3. Управление полетами Java (JMC)

3.1 Загрузите СМК здесь . Перейти ПАПКА JMC_FOLDER/корзина и запустите исполняемый файл jmc.

3.2 Файл -> Открыть файл…Выберите leak.jfr

Результаты автоматизированного анализа многообещающи; есть много предупреждений и даже набрано 100 опасных сигналов. Просмотрите раздел Распределение потоков , в нем прямо сказано, что у Потока-производителя есть проблема.

The thread performing the most allocation is likely *Producer Thread*. This is the most common allocation path for that class: •MemoryLeak.lambda$main$0() (69.4 %)

Many allocations performed by the same thread might indicate a problem in a multi-threaded program. Look at the aggregated stack traces for the thread with the highest allocation rate.

3.3 Выберите опцию память; использование памяти неуклонно увеличивается, что является четким сигналом о проблеме утечки памяти.

Кроме того, общее выделение для байта[] составляет 2,6Г, а свойства, показывающие поток событий, являются Потоком-производителем .

3.4 Выбор Живых объектов

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

Все индикаторы говорят вам, что поток Производитель вызвал утечку памяти.

4. JCMD – Запущенный процесс Java

Для запущенного процесса или приложения Java мы можем использовать cmd для запуска записи JFR и сброса данных в файл .jframe . Этот jcmd доступен в JDK11/bin , а не в JRE.

4.1 Введите cmd , в нем перечислены все доступные процессы Java и их PID.

$ jcmd

1 app.jar
47 jdk.jcmd/sun.tools.jcmd.JCmd

4.2 Для этого нам нужно отключить app.jar и этот процесс заключается в 1 .

$ jcmd 1 JFR.start duration=30s settings=profile filename=path/filename.jfr

1:
Started recording 2. The result will be written to:

{pwd}/filename.jfr

Закончив, мы можем использовать JMC для анализа файла .jfr .

4.3 jcmd запуск, проверка и сброс, все необходимое пид .

# start a 1 hour continue monitoring
$ jcmd pid JFR.start name=monitor1hour maxage=1h maxsize=100M filename=path/filename.jfr

# check what is recording now
$ jcmd pid JFR.check

pid:
Recording 3: name=monitor1hour maxsize=100.0MB maxage=1h (running)

# Dump the data for analysis even the recording is running
$ jcmd 1 JFR.dump name=monitor1hour

pid:
Dumped recording "monitor1hour", 456.1 kB written to:

Дальнейшее чтение – Обратитесь к этому Справочнику по диагностическим командам jcmd

5. Докер и JFR

Могу ли я включить JFR в процессе Java, который находится внутри контейнера Docker? Ответ – да.

P.S Протестирован с OpenJDK 11 и Docker 19.03.8.

5.1 Простой Док-файл для запуска openjdk 11:alpine контейнера и запуска app.jar файл.

FROM adoptopenjdk/openjdk11:alpine

VOLUME /tmp

ARG JAR_FILE=target/hello.jar

WORKDIR /opt/app

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","app.jar"]

5.2 Создайте и запустите докер.

# maven clean build the projects
$ mvn clean package

# create a docker image
$ docker build -t myapp:1.0 .

# Run
$ docker run -d -p 80:8080 -t myapp:1.0

5.3 Мы можем получить доступ к контейнеру docker и включить JFR для запущенного процесса Java.

$ docker ps

CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS                   NAMES
5c27d3cfdfa3        myapp:1.0    "java -jar app.jar"   25 minutes ago      Up 25 minutes       0.0.0.0:80->8080/tcp,   hungry_benz

# Access the container
$ docker exec -it 5c27d3cfdfa3 sh

/opt/app #  

5.4 Процесс тот же, jcmd , чтобы найти pid и включить JFR.

$ jcmd pid JFR.start duration=30s settings=profile filename=path/filename.jfr

5.5 Скопируйте файл .jfr из контейнера docker на ваш компьютер или хост.

# exit the docker container shell
/opt/app # exit

$ docker ps
CONTAINER ID        IMAGE               COMMAND               CREATED             STATUS              PORTS                   NAMES
5c27d3cfdfa3        myapp:1.0    "java -jar app.jar"   25 minutes ago      Up 25 minutes       0.0.0.0:80->8080/tcp,   hungry_benz

# docker cp :/file/path/within/container /host/path/target

$ docker cp 5c27d3cfdfa3:/opt/app/file.jfr /home/mkoyng/test

Готово, мы получаем файл .jar .

Дальнейшее чтение – Читать JMC_Tutorial.pdf из этого Учебник по управлению полетами JDK ; он содержит множество тематических исследований.

6. Исправить эту утечку памяти?

Ограничение LinkedBlockingQueue – это быстрое решение, но это создает еще одну проблему для производителя.

private static BlockingQueue queue = new LinkedBlockingQueue<>(100);

Если производитель может создать большое количество объектов или данных за короткое время, лучше хранить их в постоянной базе данных или очереди сообщений, например RabbitMQ . Ваш комментарий?

Скачать Исходный Код

$компакт-диск java-11

/src/main/java/com/mkyong/java11/jep328/MemoryLeak.java

/src/основные/ресурсы/jep328/утечка.jfr

Рекомендации

Оригинал: “https://mkyong.com/java/java-11-java-flight-recorder/”