«1. Обзор

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

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

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

2. async-profiler

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

$ ./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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Frame buffer usage  : 0.069%

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

7. async-profiler With IntelliJ IDEA

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

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

Мы можем настроить асинхронный профилировщик в IntelliJ IDEA, выбрав пункт меню Java Profiler в меню «Настройки/Предпочтения» \u003e «Сборка, выполнение, развертывание»:

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

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

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

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

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

Или мы можем щелкнуть на панели инструментов и выбрать Запустить \u003cимя приложения\u003e с \u003cимя конфигурации профилировщика\u003e: \u003e вариант:

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

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

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

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

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

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

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

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

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