Видео запись

Подготовка к участию в мастер-классе

Для выполнения практической части семинара вам понадобится компьютер с операционной системой Linux и root доступом. Допустимо так же использование виртуальной машины Linux в другой OS.

Для работы вам понабится perf, OpenJDK 17 и опционально Mission Control. Там же понадобиться git для подготовки.

perf

perf может быть установлен с помощь менеджера пакетов вашего Linux дистрибутива. Например для Ubuntu - apt-get install linux-tools-common linux-tools-generic linux-tools-`uname -r`.

После инсталяции проверьте работо способность. sudo perf stat sleep 10 Performance counter stats for 'sleep 10': 0.50 msec task-clock # 0.000 CPUs utilized 1 context-switches # 0.002 M/sec 0 cpu-migrations # 0.000 K/sec 61 page-faults # 0.123 M/sec 1,512,807 cycles # 3.055 GHz 1,087,665 stalled-cycles-frontend # 71.90% frontend cycles idle 899,793 instructions # 0.59 insn per cycle # 1.21 stalled cycles per insn 180,697 branches # 364.870 M/sec 9,091 branch-misses # 5.03% of all branches 10.000879454 seconds time elapsed 0.000878000 seconds user 0.000000000 seconds sys sudo perf stat -e cache-misses sleep 10 Performance counter stats for 'sleep 10': 8,278 cache-misses 10.001446216 seconds time elapsed 0.001394000 seconds user 0.000000000 seconds sys

git

git может быть установлен средствами вашего Linux дистрибутива.

OpenJDK и материалы мастер-класса

Для простоты дальнейшие инструкции будут в виде последовательности Linux комманд.

Директория для мастер-класса

export LABDIR=~/jpoint2023_perf mkdir $LABDIR cd $LABDIR

Установка OpenJDK 17

Я рекомендую использовать Liberica OpenJDK 17, но подойдёт любой дистрибутив OpenJDK 17 и выше. Не рекомендую устанавливать OpenJDK через менеджер пакетов, так как зачастую там отсутсвует отладочная информация. wget https://download.bell-sw.com/java/17.0.6+10/bellsoft-jdk17.0.6+10-linux-amd64.tar.gz tar -xf bellsoft-jdk17.0.6+10-linux-amd64.tar.gz Создание алиазов. alias java=$LABDIR/jdk-17.0.6/bin/java alias jcmd=$LABDIR/jdk-17.0.6/bin/jcmd Проверка. java --version openjdk 17.0.6 2023-01-17 LTS OpenJDK Runtime Environment (build 17.0.6+10-LTS) OpenJDK 64-Bit Server VM (build 17.0.6+10-LTS, mixed mode, sharing) jcmd 1228 jdk.jcmd/sun.tools.jcmd.JCmd

Дополнительные материалы

Иснтрумент SJK. wget https://training.ragozin.info/sjk.jar Проверка. java -jar sjk.jar --commands Restarting java with unlocked package access dexp - [Dump Export] Extract metrics from compressed dump into tabular format flame - Generates flame graph from stack traces gc - [Print GC] Print GC log like information for remote process hh - [Heap Histo] Prints class histogram, similar to jmap -histo hs - Hotspot JVM tweaks jcmd - jcmd JMX command jfr2json - [JFR 2 JSON] Flight decoder, command translates JFR files into JSON jps - [JPS] Enhanced version of JDK's jps tool mprx - JMX proxy - expose target process' MBeans for remote access mx - [MBean] MBean query and invokation mxdump - null mxping - [MXPING] Verify JMX connection to target JVM prfi - Pref import ssa - [Stack Sample Analyzer] Analyzing stack trace dumps stcap - [Stack Capture] Dumps stack traces to file for further processing stcpy - [Stack Copy] Stack dump copy/filtering utility ttop - [Thread Top] Displays threads from JVM process vminfo - [VMINFO] Dumps various from local VM Инструмент FlameGraph. git clone https://github.com/brendangregg/FlameGraph.git Материалы мастер-класса. git clone https://github.com/aragozin/jpoint_2023.git

Mission control

Я рекомендую использовать дистрибутив Liberica Mission Control.

Мастер-класс, пошаговая инструкция

Подготовка окружения

Нам потребуются два терминала для работы

Выполните следующие команды в каждом из терминалов для насторий окружения

export LABDIR=~/jpoint2023_perf cd $LABDIR alias java=$LABDIR/jdk-17.0.6/bin/java alias jcmd=$LABDIR/jdk-17.0.6/bin/jcmd

Обновим вспомогательные материалы до последней версии.

cd $LABDIR/jpoint_2023 git fetch && git reset --hard origin/main cd $LABDIR

Часть 1

1 запустить тестовую программу в отдельной консоли java -jar jpoint_2023/bias-sjk-v1.jar jpoint_2023/jboss-10k.std в консоли профалера определим pid процесса jcmd присвоим java процесса переменной для удобства export PID=1234

2 запустим perf с частотой 500 Hz sudo perf record -F 500 -p $PID -g -o perf1.data -- sleep 30 файл данных perf создан от root пользователя, меняем уровень доступа к файлу sudo chown $USER perf1.data

3 откроем файл данных perf в режиме отчёта perf report -i perf1.data

4 сгенерируем файл символов для JIT компилированного кода jcmd $PID Compiler.perfmap попробуем открыть отчёт perf снова perf report -i perf1.data посмотрим JVM стэк-трейс нашего примера jcmd $PID Thread.print "SjkDumpParser" #13 daemon prio=5 os_prio=0 cpu=38776.94ms elapsed=38.89s tid=0x00007f6b101e0860 nid=0x5e9d runnable [0x00007f6ae19cc000] java.lang.Thread.State: RUNNABLE at java.util.zip.Inflater.inflateBytesBytes(java.base@17.0.6/Native Method) at java.util.zip.Inflater.inflate(java.base@17.0.6/Inflater.java:378) - locked <0x00000007176367b8> (a java.util.zip.Inflater$InflaterZStreamRef) at java.util.zip.InflaterInputStream.read(java.base@17.0.6/InflaterInputStream.java:152) at java.util.zip.InflaterInputStream.read(java.base@17.0.6/InflaterInputStream.java:122) at java.io.DataInputStream.readByte(java.base@17.0.6/DataInputStream.java:271) at org.gridkit.jvmtool.stacktrace.StackTraceCodec.readVarInt(StackTraceCodec.java:664) at org.gridkit.jvmtool.stacktrace.StackTraceCodec$StackTraceReaderV2.readStackTrace(StackTraceCodec.java:642) at org.gridkit.jvmtool.stacktrace.StackTraceCodec$StackTraceReaderV2.readTraceInfo(StackTraceCodec.java:610) at org.gridkit.jvmtool.stacktrace.StackTraceCodec$StackTraceReaderV2.loadNext(StackTraceCodec.java:577) at info.ragozin.proflab.SjkDumpParser.run(SjkDumpParser.java:21) at info.ragozin.proflab.DemoRunner$1.run(DemoRunner.java:19) исходный код примера доступен на github

5 остановим пример и перезапустим с опцией -XX:+PreserveFramePointer java -XX:+PreserveFramePointer -jar jpoint_2023/bias-sjk-v1.jar jpoint_2023/jboss-10k.std определим новый PID jcmd export PID=1234

6 проведём новую сессию профилирования с perf sudo perf record -F 500 -p $PID -g -o perf2.data -- sleep 30 && sudo chown $USER perf2.data сгенерируем символьную информацию jcmd $PID Compiler.perfmap и откроем отчёт perf report -i perf2.data

7 используем скрипты Брендона Грега чтобы получить flame graph perf script -i perf2.data | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > perf2.svg

8 построим flame graph с использованием SJK perf script -i perf2.data | java -jar sjk.jar prfi -i - -o perf2.sjk java -jar sjk.jar flame -f perf2.sjk -o perf2.html

9 сравним результы с JFR, снимен дамп jcmd $PID JFR.start filename=$LABDIR/perf2.jfr method-profiling=high duration=1m откроем файл perf2.jfr в Mission Control, и изучем полученные данные

10 построим flame graph на основании дампа JFR java -jar sjk.jar flame -f perf1.jfr -o perf2.jfr.html

11 сравним результаты perf с JFR и классическим сэмплирование дампов потоков
проведём сэмплирование с помощью SJK java -jar sjk.jar stcap -p $PID -o perf2.dump.sjk java -jar sjk.jar flame -f perf2.dump.sjk -o perf2.dump.html

Часть 2

По-умолчанию perf использует "cycles" события для семплирования, попробуем поработать с другим типов событий.

12 Чтобы получить полный список доступных событий можно использовать команду. sudo perf list Нас интересует событие ошибки предсказателя условных переходов.
Проверим доступность этого типа событий sudo perf stat -e branch-miss sleep 10

13 для демонстрации сэмплирования по branch-miss событиям (ошибкам предсказания условных переходов) запустим следующий пример коммандой ниже java -XX:+PreserveFramePointer -cp jpoint_2023/ PositiveSumBench зафиксируем PID jcmd export PID=1234

14 проведем профилирование в стандартном режиме sudo perf record -F 500 -p $PID -g -o perf3.data -- sleep 30 && sudo chown $USER perf3.data

15 теперь проведём повторное профилирование, используя события ошибки предсказания перехода для сэмплирования sudo perf record -F 500 -p $PID -g -o perf4.data -e branch-miss -- sleep 30 && sudo chown $USER perf4.data экспортируем JIT символы jcmd $PID Compiler.perfmap

16 запустим по очереди отчёт по обоим файлам полученым в разных режимах perf report -i perf3.data perf report -i perf4.data

Часть 3

17 запустим новый тестовый пример java -XX:+PreserveFramePointer -jar jpoint_2023/javaref-bench-finalizer.jar зафиксируем PID jcmd export PID=1234

18 проведём сэмплирование sudo perf record -F 500 -p $PID -g -o perf5.data -- sleep 30 && sudo chown $USER perf5.data jcmd $PID Compiler.perfmap

19 подготовим flame graph с помощью SJK perf script -i perf5.data | java -jar sjk.jar prfi -i - -o perf5.sjk java -jar sjk.jar flame -f perf5.sjk -o perf5.html откроем полученный flame graph

Часть 4

20 начнём работу с новым примером java -XX:+PreserveFramePointer -cp jpoint_2023/ CryptoBench зафиксируем PID jcmd export PID=1234

21 проведём сэплирование JFR и perf для дальнейшего сравнения jcmd $PID JFR.start filename=$LABDIR/perf6.jfr method-profiling=high duration=30s sudo perf record -F 500 -p $PID -g -o perf6.data -- sleep 30 && sudo chown $USER perf6.data jcmd $PID Compiler.perfmap

22 построим "огненые" графы и сравним их java -jar sjk.jar flame -f perf6.jfr -o perf6.jfr.html perf script -i perf6.data | java -jar sjk.jar prfi -i - -o perf6.sjk java -jar sjk.jar flame -f perf6.sjk -o perf6.html откроем файлы perf6.jfr.html и perf6.html для сравнения результатов

23 перезапустим пример с опцием -XX:+DebugNonSafepoints java -XX:+PreserveFramePointer -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints -cp jpoint_2023/ CryptoBench зафиксируем PID jcmd export PID=1234

24 повторим профилирование и построение окчётов для JFR и perf jcmd $PID JFR.start filename=$LABDIR/perf7.jfr method-profiling=high duration=30s sudo perf record -F 500 -p $PID -g -o perf7.data -- sleep 30 && sudo chown $USER perf7.data jcmd $PID Compiler.perfmap java -jar sjk.jar flame -f perf7.jfr -o perf7.jfr.html perf script -i perf7.data | java -jar sjk.jar prfi -i - -o perf7.sjk java -jar sjk.jar flame -f perf7.sjk -o perf7.html откроем файлы perf7.jfr.html и perf7.html и посмотрим как изменились результаты

Дополнительные материалы

  • Слайды использованные в мастер классе
  • https://bell-sw.com/announcements/2022/04/07/how-to-use-perf-to-monitor-java-performance/ - статья по теме мастер-класса
  • https://blog.ragozin.info/2019/03/lies-darn-lies-and-sampling-bias.html - статья на тему sampling bias
  • https://github.com/jvm-profiling-tools/perf-map-agent - агент для экспорта символов для старых версий JVM
  • Ссылки из дискуссионной зоны

  • https://www.youtube.com/watch?v=H6glyrKQlg8
  • https://www.youtube.com/watch?v=bTDmpwhwy3E
  • https://youtu.be/WnRngFMBe7w
  • https://youtu.be/UzM4S1hXNtU
  • https://netflixtechblog.com/java-in-flames-e763b3d32166
  • https://netflixtechblog.com/netflix-flamescope-a57ca19d47bb
  • https://github.com/Netflix/flamescope
  • https://www.youtube.com/watch?v=tTgHsmxofeU