火焰图成为调查当今应用程序(不仅仅是用 Java 编写)问题的事实标准。火焰图可以提供许多有趣的见解,并可以为开发人员提供有价值的提示,以改进其应用程序的执行。然而,仍然有很多开发人员不使用它们,即使生成火焰图比以往任何时候都容易。

在本文中,我将演示几个示例,说明在 Java 中开始调查应用程序是多么容易。如果您不了解有关火焰图可视化的详细信息,请访问Brendan Gregg撰写的一篇关于火焰图的精彩文章。

让我们开始使用 Jeffrey CLI

我将使用 Jeffrey CLI(命令行界面)工具来生成火焰图。对于您的终端来说,这是一个非常新且易于使用的工具,它接受JFR(JDK Flight Recorder)记录,并从存储在二进制文件中的事件中生成火焰图。

Jeffrey 获取 JFR 文件并将包含的图形和数据生成到一个 HTML 文件中,因此,您有一种非常方便的方式与同事共享它。

为了完整起见,我需要提一下还有 Jeffrey App。这是一个基于 Web 的解决方案,具有正在运行的 Java 后端,动态提供数据,并在 Jeffrey CLI 工具上包含一些其他有趣的功能。但是,今天让我们只关注生成火焰图。

基于服务器的解决方案 Jeffrey App 存在一些限制,这些限制来自生成文件的“静态性质”。Jeffrey App 在后台在服务器上动态生成数据。以下功能在 CLI 解决方案中不可用。但是,有一些针对这些缺点的缓解措施:

  • 未提供时间序列图中的动态搜索。我们可以使用 CLI 参数在执行命令时分割时间序列图。
  • 时间序列图的缩放不会传播到火焰图。

如果您从未听说过 JFR 录音,那么让我们从简要介绍开始。JFR 代表 JDK Flight Recorder,它是一种内置功能,用于在 OpenJDK 构建中从 JVM 和 Java 应用程序收集事件。它使用所有收集的事件 + 元数据生成优化的二进制文件,以正确解析文件中的事件。在撰写本文时,有两种常见的方法来生成此二进制文件:

  • 直接在 JVM 中启用该功能(JDK Flight Recorder 用户指南)
  • 使用 Async Profiler,它可以用自己的事件替换某些事件(Async-Profiler 的设置) 

无论采用哪种方法生成的二进制文件都具有完全相同的结构,并且可以由 Jeffrey CLI 处理。

下载并启动

最直接的方法是直接从 GitHub 下载。

我将使用 Jeffrey 的测试应用程序中预生成的录音。检查录音并将jeffrey-cli.jar复制到同一文件夹,以使命令更简单、更短。上述 GitHub 存储库中的记录是使用 AsyncProfile 生成的,AsyncProfile 使用自己的 CPU () 和分配 () 事件,这些事件将在下面的示例中使用。jdk.ExecutionSamplejdk.ObjectAllocationInNewTLAB

jeffrey-cli.jar 只是一个可执行的 jar 文件,它执行带有一些参数的基本命令来指定行为。

根据 ,在撰写这篇博文时,我们可以生成火焰图、亚秒图及其微分图(比较 JFR 记录中相同事件类型之间的比较)。在本文中,让我们只关注火焰图。HELP command

$ java -jar jeffrey-cli.jar --help
Usage:  [-hV] [COMMAND]
Generates a flamegraph according to the selected event-type
  -h, --help      Show this help message and exit.
  -V, --version   Print version information and exit.
Commands:
  flame            Generates a Flamegraph (default: jdk.ExecutionSample)
  flame-diff       Generates a Differential Flamegraph (default: jdk.ExecutionSample)
  events           List all event-types containing a stacktrace for building a flamegraph
  sub-second       Generates Sub-Second graph (the first 5 minutes)
  sub-second-diff  Generates Differential Sub-Second graph (the first 5 minutes)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
具有默认事件类型的火焰图

有多个参数可以澄清生成的输出,让我们关注主要参数。执行命令以显示以下信息。flame --help

$ java -jar jeffrey-cli.jar flame --help
Usage:  flame [-htVw] [--with-timeseries] [-e=<eventType>] [--end-time=<endTime>] [-o=<outputFile>] [-s=<searchPattern>] [--start-time=<startTime>] <jfr_file>
Generates a Flamegraph (default: jdk.ExecutionSample)
      <jfr_file>             one JFR file for fetching events
  -e, --event-type=<eventType>
                             Selects events for generating a flamegraph (e.g. jdk.ExecutionSample)
      --end-time=<endTime>   Relative end in milliseconds from the beginning of the JFR file
  -h, --help                 Show this help message and exit.
  -o, --output=<outputFile>  Path to the file with the generated flamegraph (default is the current folder with a filename '<jfr-name>.html')
  -s, --search-pattern=<searchPattern>
                             Only for timeseries (timeseries cannot dynamically searches in the generated file, only the flamegraph can)
      --start-time=<startTime>
                             Relative start in milliseconds from the beginning of the JFR file
  -t, --thread               Groups stacktraces omitted on the particular thread
  -V, --version              Print version information and exit.
  -w, --weight               Uses event's weight instead of # of samples (currently supported: jdk.ObjectAllocationSample, jdk.ObjectAllocationInNewTLAB, jdk.
                               ObjectAllocationOutsideTLAB, jdk.ThreadPark, jdk.JavaMonitorWait, jdk.JavaMonitorEnter)
      --with-timeseries      Includes Timeseries graph with a Flamegraph (it's `true` by default, set `false` to have only the Flamegraph)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.

下面最简单的命令将 CPU 火焰图(基于 )生成到与记录名称相同的文件夹中(您可以使用参数来指定输出的文件名和路径)。jdk.ExecutionSampleoutput

java -jar jeffrey-cli.jar flame jeffrey-persons-full-direct-serde.jfr
Generated: <path>/jeffrey-recordings/jeffrey-persons-full-direct-serde.html
  • 1.
  • 2.

在您最喜欢的浏览器中打开 <path>/jeffrey-recordings/jeffrey-persons-full-direct-serde.html

如何在 Java 中生成火焰图_开发语言

特定事件类型的火焰图

下面的另一个示例使用带有选项的特定事件类型。由于我们知道记录是使用带有选项的 async-profiler 生成的,因此我们需要使用 as the 来获得适当的结果。weightallocjdk.ObjectAllocationInNewTLABevent-type

weight

$ java -jar jeffrey-cli.jar flame --event-type=jdk.ObjectAllocationInNewTLAB --weight jeffrey-persons-full-direct-serde.jfr
Generated: <path>/jeffrey-recordings/jeffrey-persons-full-direct-serde.html
  • 1.
  • 2.

如何在 Java 中生成火焰图_java_02

按线程分组的火焰图

在某些情况下,生成一个图表很有用,其中样本按给定样本生成的特定线程进行分组(特别是对于挂钟样本,但是,它对其他类型也有意义)。

$ java -jar jeffrey-cli.jar flame --thread jeffrey-persons-full-direct-serde.jfr
Generated: <path>/jeffrey-recordings/jeffrey-persons-full-direct-serde.html
  • 1.
  • 2.

如何在 Java 中生成火焰图_java_03

在时间序列和火焰图中搜索

如前所述,由于其静态特性,因此无法直接从生成的图形中使用缩放和搜索。但是,至少我们可以使用搜索模式选项生成图形,以将时间序列图拆分为两个系列:

  1. 包含搜索模式的示例
  2. 其余不匹配的样本

我们在样本中搜索模式,以指出记录期间的编译开销。Compile

$ java -jar jeffrey-cli.jar flame --search-pattern=Compile jeffrey-persons-full-direct-serde.jfr
Generated: <path>/jeffrey-recordings/jeffrey-persons-full-direct-serde.html
  • 1.
  • 2.

如何在 Java 中生成火焰图_jar_04