Java性能分析工具AsyncProfiler

2023年 9月 29日 92.6k 0

Snipaste_2023-09-28_23-08-48.png

1.什么是async-profiler

简介

async-profiler是一个低开销的 Java 采样分析器,它不会遇到安全点偏差问题。它使用HotSpot 特定的 API 来收集堆栈信息,追踪内存分配。该分析器可与 OpenJDK、Oracle JDK ,以及其他基于 HotSpot JVM 的 Java 运行时配合使用。

async-profiler 可以跟踪以下类型的事件:

  • CPU周期
  • 硬件和软件性能计数器,例如缓存未命中、分支未命中、页面错误、上下文切换等。
  • Java 堆中的分配
  • Contented lock尝试,包括 Java 对象监视器和 ReentrantLocks

下载地址

  • Linux x64 (glibc): async-profiler-2.9-linux-x64.tar.gz
  • Linux x64 (musl): async-profiler-2.9-linux-musl-x64.tar.gz
  • Linux arm64: async-profiler-2.9-linux-arm64.tar.gz
  • macOS x64/arm64: async-profiler-2.9-macos.zip

支持的平台

  • Linux / x64 / x86 / arm64 / arm32 / ppc64le
  • macOS / x64 / arm64

2.功能介绍

2.1. CPU profiling

在这个模式中,profiler收集stack traces的样本,其中包括Java methods,native calls, JVM代码和内核函数。

普遍的方法是接收 perf_events 生成的stack traces,然后将它们与 AsyncGetCallTrace 生成的调用堆栈进行匹配,以便生成 Java 和native code 的准确配置文件。此外,在 AsyncGetCallTrace 失败的某些极端情况下,async-profiler 提供了一种解决方法,可以恢复stack traces。

与另一种方式:直接将 perf_events 与 Java agent 结合使用,将地址转换为 Java method name 相比,该方法具有以下优点:

  • 适用于较旧的 Java 版本,因为它不需要 -XX:+PreserveFramePointer,该选项仅在 JDK 8u60 及更高版本中可用。
  • 不会引入 -XX:+PreserveFramePointer 的性能开销,在极少数情况下,这种性能开销可能高达 10%。
  • 不需要生成映射文件来将 Java 代码地址映射到方法名称。
  • 与解释器框架一起使用。
  • 不需要写出 perf.data 文件以在用户空间脚本中进行进一步处理。

2.2 ALLOCATION profiling

除了检测检测消耗 CPU 的代码,async-profiler可以配置为收集分配了最大堆内存的调用站点。

async-profiler 不使用字节码检测或昂贵的 DTrace 探针等侵入性技术,这些技术会对性能产生重大影响。它也不会影响逃逸分析或防止 JIT 优化(例如分配消除)。它仅测量实际的堆分配。

该profiler具有 TLAB 驱动的采样功能。它依赖于 HotSpot 特定的回调来接收两种类型的通知:

  • 当在新创建的 TLAB 中分配对象时(火焰图中的水色框)
  • 当对象分配在 TLAB 外部的慢速路径上时(棕色帧)。

这意味着不会计算每个分配,而只会计算每 N kB 的分配,其中 N 是 TLAB 的平均大小。这使得堆采样非常便宜并且适合生产。另一方面,收集的数据可能不完整,尽管在实践中它通常会反映最高的分配来源。

我们可以使用 --alloc 选项调整采样间隔。例如,--alloc 500k 将在平均分配 500 KB 空间后进行一次采样。但是,小于 TLAB 大小的间隔将不会生效。

支持的最低 JDK 版本是 7u40,其中出现了 TLAB 回调。

Installing Debug Symbols

在 JDK 11 之前,分配分析器需要 HotSpot 调试符号。 Oracle JDK 已将它们嵌入到 libjvm.so中,但在 OpenJDK 版本中,它们通常在单独的包中提供。例如,要在 Debian / Ubuntu 上安装 OpenJDK 调试符号,请运行:

apt install openjdk-8-dbg

或者对于 OpenJDK 11:

apt install openjdk-11-dbg

gdb 工具可用于验证 libjvm 库的调试符号是否已正确安装。例如在 Linux 上:

gdb $JAVA_HOME/lib/server/libjvm.so -ex 'info address UseG1GC'

该命令的输出将包含Symbol "UseG1GC" is at 0xxxxx 或者No symbol "UseG1GC" in current context

2.3. Wall-clock profiling

-e wall 选项告诉 async-profiler 在每个给定时间段对所有线程进行同等采样,无论线程状态如何:正在运行、睡眠或阻塞。例如,这在分析应用程序启动时间时非常有用。

Wall-clock profiler在每线程模式下最有用:-t

举例:

asprof -e wall -t -i 5ms -f result.html 8983

2.4.Java method profiling

-e ClassName.methodName 选项检测给定的 Java method,以便通过堆栈跟踪记录该方法的所有调用。

比如:-e java.util.Properties.getProperty 将分析调用 getProperty 方法的所有位置。

这种方式仅支持non-native Java methods。如果想要分析native method,需要使用hardware breakpoint event 事件,例如 -e Java_java_lang_Throwable_fillInStackTrace

请注意,如果您在运行时附加 async-profiler,non-native Java methods的第一次检测可能会导致所有已编译方法的去优化。后续检测仅刷新相关代码。如果将 async-profiler 作为agent附加,则不会发生大规模 CodeCache 刷新。

下面是一些对于分析有用的 native methods:

  • G1CollectedHeap::humongous_obj_allocate:跟踪 G1 GC 的巨大分配
  • JVM_StartThread:跟踪新线程的创建
  • Java_java_lang_ClassLoader_defineClass1:跟踪类加载

3.如何安装

(1)确保环境变量JAVA_HOMEGCC正常,手动make即可。

(1)直接下载二进制文件。

cd async-profiler/
make
chmod +x ./profiler.sh
./profiler.sh
Usage: ./profiler.sh [action] [options] 
Actions:
  start             start profiling and return immediately
  resume            resume profiling without resetting collected data
  stop              stop profiling
  dump              dump collected data without stopping profiling session
  check             check if the specified profiling event is available
  status            print profiling status
  meminfo           print profiler memory stats
  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
  -t                profile different threads separately
  -s                simple class names instead of FQN
  -g                print method signatures
  -a                annotate Java methods
  -l                prepend library names
  -o fmt            output format: flat|traces|collapsed|flamegraph|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    FlameGraph title
  --minwidth pct    skip frames smaller than pct%
  --reverse         generate stack-reversed FlameGraph / Call tree

  --loop time       run profiler in a loop
  --alloc bytes     allocation profiling interval in bytes
  --live            build allocation profile from live objects only
  --lock duration   lock profiling threshold in nanoseconds
  --total           accumulate the total value (time, bytes, etc.)
  --all-user        only include user-mode events
  --sched           group threads by scheduling policy
  --cstack mode     how to traverse C stack: fp|dwarf|lbr|no
  --begin function  begin profiling when function is executed
  --end function    end profiling when function is executed
  --ttsp            time-to-safepoint profiling
  --jfrsync config  synchronize profiler with JFR recording
  --lib path        full path to libasyncProfiler.so in the container
  --fdtransfer      use fdtransfer to serve perf requests
                    from the non-privileged target

 is a numeric process ID of the target JVM
      or 'jps' keyword to find running JVM automatically
      or the application's name as it would appear in the jps tool

Example: ./profiler.sh -d 30 -f profile.html 3456
         ./profiler.sh start -i 999000 jps
         ./profiler.sh stop -o flat jps
         ./profiler.sh -d 5 -e alloc MyAppName

4.基本用法

jps
18023 Jps
18006 Hello
sudo ./profiler.sh start 18006
sudo ./profiler.sh start 18006

结果如下:

--- Execution profile ---
Total samples       : 2189

--- 12710254800 ns (58.06%), 1271 samples
  [ 0] libmCos
  [ 1] Hello.for_loop
  [ 2] Hello.loopbig
  [ 3] Hello.main

--- 5710052600 ns (26.08%), 571 samples
  [ 0] libmSin
  [ 1] Hello.for_loop
  [ 2] Hello.loopbig
  [ 3] Hello.main

--- 1859944300 ns (8.50%), 186 samples
  [ 0] libmCos
  [ 1] Hello.for_loop
  [ 2] Hello.loopsmall
  [ 3] Hello.main

--- 760076900 ns (3.47%), 76 samples
  [ 0] libmSin
  [ 1] Hello.for_loop
  [ 2] Hello.loopsmall
  [ 3] Hello.main

--- 699998700 ns (3.20%), 70 samples
  [ 0] Hello.for_loop
  [ 1] Hello.loopbig
  [ 2] Hello.main

--- 150021300 ns (0.69%), 15 samples
  [ 0] Hello.for_loop
  [ 1] Hello.loopsmall
  [ 2] Hello.main

          ns  percent  samples  top
  ----------  -------  -------  ---
 14570199100   66.56%     1457  libmCos
  6470129500   29.56%      647  libmSin
   850020000    3.88%       85  Hello.for_loop

**注意!**可以使用以下内容代替 pid (18006):

  • 关键字 jps,它将使用最近启动的 Java 进程。
  • jps 输出中显示的应用程序名称:例如Hello

当然,我们也可以只用单个命令完成统计(指定 -d(持续时间)参数,在固定时间段内分析应用程序)。举例:

sudo ./profiler.sh -d 30 18006

默认情况下,分析频率为 100Hz(每 10 毫秒 CPU 时间)。

5.以Agent的方式启动

如果你需要在 JVM 启动后立即分析某些代码,可以在命令行上附加 async-profiler 作为代理,而不是使用 asprof。例如:

java -agentpath:/path/to/libasyncProfiler.so=start,event=cpu,file=profile.html ...

Agent library通过 JVMTI 参数接口进行配置。参数字符串的格式在源代码中描述。 asprof 实际上就是将命令行参数转换为该格式。比如,-e wall 转换为 event=wall-f profile.html 转换为 file=profile.html 等。然而,有些参数是由 asprof 直接处理的。例如。 -d 5 其实包含 3 个操作:

(1)使用启动命令附加探查器代理
(2)休眠 5 秒
(3)然后使用停止命令再次附加代理

6.多个事件

使用async profiler,我们可以同时分析 CPU、allocations 和locks 。当然,也可以选择任何其他执行事件来代替 CPU:wall-clock、perf event、tracepoint、Java method 等。

唯一同时支持多个事件的输出格式是 JFR。记录将包含以下事件类型:

  • jdk.ExecutionSample
  • jdk.ObjectAllocationInNewTLAB (alloc)
  • jdk.ObjectAllocationOutsideTLAB (alloc)
  • jdk.JavaMonitorEnter (lock)
  • jdk.ThreadPark (lock)

要开始一起分析cpu + allocations + locks ,命令如下:

asprof -e cpu,alloc,lock -f profile.jfr ...

或使用 --alloc--lock 参数以及所需的阈值:

asprof -e cpu --alloc 2m --lock 10ms -f profile.jfr ...

同样,当作为代理启动探查器时:

-agentpath:/path/to/libasyncProfiler.so=start,event=cpu,alloc=2m,lock=10ms,file=profile.jfr

7.火焰图可视化

async-profiler 提供开箱即用的 Flame Graph 支持。指定 -o Flamegraph 参数将分析结果转储为交互式 HTML 火焰图。此外,如果目标文件名以 .html 结尾,则会自动选择火焰图输出格式。

jps
9234 Jps
8983 Hello
asprof -d 30 -f /tmp/flamegraph.html 8983

image-20230928213514878

8. Profiler Options

  • start - 以半自动模式开始分析,即分析器将运行直到显式调用stop命令。
  • resume - 启动或恢复之前已停止的分析会话。所有收集的数据仍然有效。分析选项不会在会话之间保留,应重新指定。
  • stop - 停止分析并打印报告。
  • dump - 转储收集的数据而不停止分析会话。
  • check - 检查指定的分析事件是否可用。
  • status - 打印分析状态:分析器是否处于活动状态以及持续时间。
  • meminfo - 打印已用内存统计信息。
  • list - 显示可用于目标进程(如果指定了 PID)或默认 JVM 的分析事件列表。
  • -d N - 分析持续时间(以秒为单位)。如果未给出startresumestopstatus选项,profiler将运行指定的时间段,然后自动停止。
  • -e event - 分析事件:cpu、alloc、lock、cache-misses 等。使用 list 查看可用事件的完整列表。
  • -o fmt - 指定分析结束时转储哪些信息。 fmt 可以是以下选项之一:
    • traces[=N] - 转储call traces (最多 N 个 samples)
    • flat[=N] - 转储 flat profile(前N个热门方法);可以与 traces 结合使用,例如traces=200,flat=200
    • jfr - 以 Java Mission Control 可读的 Java Flight Recorder 格式转储事件
    • collapsed - 以 FlameGraph 脚本使用的格式转储折叠的调用跟踪。这是调用堆栈的集合,其中每一行都是以分号分隔的帧列表,后跟一个计数器
    • flamegraph - 生成 HTML 格式的火焰图。
    • tree - 生成 HTML 格式的调用树。
      --reverse 生成回溯视图。

还有很多,自行查阅相关文档…………

参考

[1] github.com/async-profi…

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论