Перейти к основному содержимому

Руководство по асинхронному профайлеру

· 7 мин. чтения

1. Обзор

Профилировщики выборки Java обычно разрабатываются с использованием интерфейса инструментов JVM (JVMTI) и собирают трассировки стека в безопасной точке. Следовательно, эти профилировщики выборки могут страдать от проблемы смещения точки безопасности .

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

В этом руководстве мы рассмотрим async-profiler вместе с различными методами профилирования, которые он предлагает.

2. асинхронный профилировщик

async-profiler — это профилировщик выборки для любого JDK на базе HotSpot JVM . Он имеет низкие накладные расходы и не зависит от JVMTI.

Это позволяет избежать проблемы смещения точки сохранения, используя API AsyncGetCallTrace , предоставляемый HotSpot JVM, для профилирования путей кода Java, и perf_events Linux для профилирования путей собственного кода.

Другими словами, профилировщик сопоставляет стеки вызовов как кода Java, так и путей собственного кода, чтобы получить точные результаты.

3. Настройка

3.1. Монтаж

Сначала мы загрузим последнюю версию async-profiler для нашей платформы. В настоящее время он поддерживает только платформы Linux и macOS.

После загрузки мы можем проверить, работает ли он на нашей платформе:

$ ./profiler.sh --version
Async-profiler 1.7.1 built on May 14 2020
Copyright 2016-2020 Andrei Pangin

Всегда полезно заранее проверить все параметры, доступные с помощью async-profiler :

$ ./profiler.sh
Usage: ./profiler.sh [action] [options] 
Actions:
start start profiling and return immediately
resume resume profiling without resetting collected data
stop stop profiling
check check if the specified profiling event is available
status print profiling status
list list profiling events supported by the target JVM
collect collect profile for the specified period of time
and then stop (default action)
Options:
-e event profiling event: cpu|alloc|lock|cache-misses etc.
-d duration run profiling for seconds
-f filename dump output to
-i interval sampling interval in nanoseconds
-j jstackdepth maximum Java stack depth
-b bufsize frame buffer size
-t profile different threads separately
-s simple class names instead of FQN
-g print method signatures
-a annotate Java method names
-o fmt output format: summary|traces|flat|collapsed|svg|tree|jfr
-I include output only stack traces containing the specified pattern
-X exclude exclude stack traces with the specified pattern
-v, --version display version string

--title string SVG title
--width px SVG width
--height px SVG frame height
--minwidth px skip frames smaller than px
--reverse generate stack-reversed FlameGraph / Call tree

--all-kernel only include kernel-mode events
--all-user only include user-mode events
--cstack mode how to traverse C stack: fp|lbr|no

is a numeric process ID of the target JVM
or 'jps' keyword to find running JVM automatically

Многие из показанных опций пригодятся в последующих разделах.

3.2. Конфигурация ядра

При использовании async-profiler на платформе Linux мы должны убедиться, что наше ядро сконфигурировано для захвата стеков вызовов с использованием perf_events всеми пользователями:

Во-первых, мы установим perf_event_paranoid в 1, что позволит профилировщику собирать информацию о производительности:

$ sudo sh -c 'echo 1 >/proc/sys/kernel/perf_event_paranoid'

Затем мы установим для kptr_restrict значение 0, чтобы снять ограничения на раскрытие адресов ядра:

$ sudo sh -c 'echo 0 >/proc/sys/kernel/kptr_restrict'

Однако асинхронный профилировщик будет работать сам по себе на платформе macOS.

Теперь, когда наша платформа готова, мы можем создать приложение для профилирования и запустить его с помощью команды Java:

$ java -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -jar path-to-jar-file

Здесь мы запустили наше приложение для профилирования, используя флаги JVM -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints , которые настоятельно рекомендуются для получения точных результатов .

Теперь, когда мы готовы профилировать наше приложение, давайте рассмотрим различные типы профилирования, поддерживаемые async-profiler .

4. Профилирование ЦП

Async-profiler собирает образцы трассировки стека методов Java, включая код JVM, собственный класс и функции ядра, при профилировании ЦП.

Давайте профилируем наше приложение, используя его PID:

$ ./profiler.sh -e cpu -d 30 -o summary 66959
Started [cpu] profiling
--- Execution profile ---
Total samples : 28

Frame buffer usage : 0.069%

Здесь мы определили событие профилирования процессора с помощью параметра -e . Затем мы использовали параметр -d <duration> для сбора выборки в течение 30 секунд.

Наконец, параметр -o полезен для определения выходного формата, такого как сводка, HTML, трассировка, SVG и дерево .

Давайте создадим вывод HTML во время профилирования ЦП нашего приложения:

$ ./profiler.sh -e cpu -d 30 -f cpu_profile.html 66959

./2f86a62e7bf30a8a8c17c1a3f4dd28e0.png

Здесь мы видим, что вывод HTML позволяет нам расширять, сворачивать и искать образцы.

Кроме того, async-profiler поддерживает пламенные графики «из коробки» .

Давайте создадим график пламени, используя расширение файла .svg для профиля ЦП нашего приложения:

$ ./profiler.sh -e cpu -d 30 -f cpu_profile.svg 66959

./9ee96dd730b9e9c9604c8918a8e8d19e.png

Здесь полученный график пламени показывает пути кода Java зеленым цветом, C++ желтым цветом и пути системного кода красным цветом.

5. Профилирование распределения

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

async-profiler использует метод выборки на основе TLAB (Thread Local Allocation Buffer) для сбора выборок выделения кучи выше среднего размера TLAB.

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

$ ./profiler.sh -e alloc -d 30 -f alloc_profile.svg 66255

./31a5565d070a647f660b701362df95e9.png

Здесь мы видим, что клонирование объекта выделило большую часть памяти, что иначе трудно заметить при просмотре кода.

6. Профилирование настенных часов

Кроме того, async-profiler может отбирать все потоки независимо от их статуса — например, запущенные, спящие или заблокированные — с помощью профиля настенных часов.

Это может оказаться удобным при устранении неполадок во время запуска приложения.

Определив событие стены , мы можем настроить профилировщик для сбора образцов всех потоков:

$ ./profiler.sh -e wall -t -d 30 -f wall_clock_profile.svg 66959

./13c3be472fb984e757737c37a7449545.png

Здесь мы использовали профилировщик настенных часов в режиме для каждого потока с использованием параметра -t , который настоятельно рекомендуется при профилировании всех потоков.

Кроме того, мы можем проверить все события профилирования, поддерживаемые нашей JVM, используя параметр списка :

$ ./profiler.sh list 66959
Basic events:
cpu
alloc
lock
wall
itimer
Java method calls:
ClassName.methodName

7. асинхронный профилировщик с IntelliJ IDEA

В IntelliJ IDEA реализована интеграция с async-profiler в качестве инструмента профилирования для Java .

7.1. Конфигурации профилировщика

Мы можем настроить async-profiler в IntelliJ IDEA, выбрав пункт меню Java Profiler в Settings/Preferences > Build, Execution, Deployment :

./983da8985e4388329eac4828ad99dc4f.png

Кроме того, для быстрого использования мы можем выбрать любую предопределенную конфигурацию, например профилировщик ЦП и профилировщик распределения, которые предлагает IntelliJ IDEA .

Точно так же мы можем скопировать шаблон профилировщика и отредактировать параметры агента для конкретных случаев использования.

7.2. Приложение профиля с использованием IntelliJ IDEA

Есть несколько способов проанализировать наше приложение с помощью профилировщика.

Например, мы можем выбрать приложение и выбрать « Запустить <имя приложения> с опцией <имя конфигурации профилировщика>»:

./e0fcb47bbe906e5a17868e8226d47e2b.png

Или мы можем щелкнуть на панели инструментов и выбрать параметр « Запустить <имя приложения> с <имя конфигурации профилировщика> »:

./b753e65dd6ec775e259a345253bc1168.png

Или, выбрав параметр « Выполнить с профилировщиком» в меню « Выполнить» , а затем выбрав < имя конфигурации профилировщика> :

./8716326ad08989c832962475d7e34bbf.png

Кроме того, в меню « Выполнить » мы видим параметр « Прикрепить профайлер к процессу ». Он открывает диалог, который позволяет нам выбрать процесс для присоединения: ``

./0ce8d00879a87323d67d706c9bc6148c.png

После того, как наше приложение будет профилировано, мы можем проанализировать результат профилирования с помощью панели инструментов Profiler в нижней части IDE.

Результат профилирования нашего приложения будет выглядеть так:

./cae2c96200b7287b6c8a0cb8d0b7d437.png

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

Кроме того, мы можем выбрать опцию Profiler в меню View > Tool Windows , чтобы увидеть результаты:

./7a41354b04c29bfb30de2e94d68c0efc.png

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

В этой статье мы рассмотрели async-profiler , а также несколько методов профилирования.

Во-первых, мы увидели, как настроить ядро при использовании платформы Linux, и несколько рекомендуемых флагов JVM для начала профилирования нашего приложения для получения точных результатов.

Затем мы рассмотрели различные типы методов профилирования, такие как ЦП, распределение и настенные часы.

Наконец, мы профилировали приложение с помощью асинхронного профилировщика с помощью IntelliJ IDEA.