https://zhuanlan.zhihu.com/p/374861737
目前有几种工具可用于分析应用程序的性能并显示结果。传统上,这些结果要么以某种表格形式显示,要么以平面形式显示,要么以树视图的形式显示。火焰图是比较新的,并采取一个新的角度来显示结果。
此外,可以在不同的级别上生成火焰图;工具连接到JVM,但也可以在(Linux/MacOS)OS级别上连接。
在这篇文章中,我希望给你一些关于火焰图是什么,如何读取它们,以及创建它们可用的工具的一些想法。
什么是火焰图?
火焰图是Brendan Gregg发明的一种显示轮廓结果的方法。或者,正如他所说:
火焰图是抽样堆栈痕迹的可视化,它允许快速识别热代码路径.
下面您将看到一个来自SpringBoot演示应用程序的示例火焰图。
火焰图一般是根据取样剖面仪的结果生成的。这些将创建堆栈样本,然后可以转换/折叠以获得生成火焰图的正确格式。然后,该图形由BrendanGregg的FlameGraphTool生成,这是一个简单的Perl脚本,将输出一个SVG映像。
SVG图片的好处在于它可以被搜索和过滤(不幸的是,在这篇博客文章中是不可能的),这使得它非常容易遍历。另外,生成火焰图的输入是一个简单的文本文件,可以很容易地被黑客攻击以添加/过滤信息!
火焰图将提供以下信息:
值得注意的是X轴!=时间的流逝。堆栈帧按字母顺序从左到右排序。所以只看宽度,而不是轴上的位置。
将其与下面的JProfiler树视图进行比较,在该视图中,概述丢失的速度要快得多。
但是,在查看分析结果时,请确保您知道正在查看的是什么。某些分析器,例如,遭受‘安全点抽样偏差’(因此,我们将使用诚实的Profiler稍后)。类似地,在使用插装时,由于额外的开销,这可能会导致结果混乱,因此不建议使用。
火焰图真正好的地方是,您可以只为Java生成它们,也可以在OS级别(Linux和OSX)生成它们,以获得更低级别的信息。以前,由于信息丢失,系统分析器无法填充整个Java堆栈。由于JDK 8更新60 Build 19(也包括JDK 9),框架指针信息通过以下选项固定-XX:+保护框架。稍后,我将展示如何生成JVM仅火焰图以及OS级火焰图。
对于这个例子,我们将使用同事构建的一个非常简单的应用程序来进行一些CPU分析:轮廓仪Schulung
。虽然这不会以任何方式产生花哨的结果,但运行起来很简单,只运行很短的时间,并且使JVM和OS级分析结果之间的差异非常明显。可以使用Maven构建应用程序。
为了生成Java火焰图,我们将使用诚实的Profiler。除了不受SafePoint偏见的困扰外,分析器还包括转换堆栈信息的代码,以便为FlameGraphTool的处理做好准备(尽管这有点隐藏)。
第一步是在执行应用程序时包括诚实的Profiler代理。完整的命令如下所示:
java -agentpath:/honest-profiler/liblagent.so=interval=7,logPath=/tmp/log.hpl -cp Cpu-0.0.1-SNAPSHOT.jar de.codecentric.training.javaprofiling.cpu.PrimeFinderRunner 1000000 1 1
申请可立即启动。完成后,这将输出/tmp/log.hpl包含原始堆栈信息的文件。
这需要转换为折叠堆栈数据是这样的:
java -cp /honest-profiler/honest-profiler.jar com.insightfullogic.honest_profiler.ports.console.FlameGraphDumperApplication /tmp/log.hpl /tmp/log.folded
根据这些折叠的堆栈信息,我们现在可以创建火焰图:
/FlameGraph/flamegraph.pl /tmp/log.folded > /tmp/flamegraph-java.svg
如果一切顺利,就会产生以下图表:
您可能会得到一个稍微不同的结果,因为小代码正在内联。这可以通过使用以下选项来解决-XX:InlineSmallCode=100为了防止内联,除了非常小的代码块。或使用其他(首选)选项:-XX:+解锁诊断VMOptions-XX:+DebugnonSafepoint.
正如你在图中所看到的,也有一些“未知”AGCT.未知的Java样本是可见的。无法映射这些信息来捕获堆栈信息,VisualVM等工具根本不会显示它们。在接下来的步骤中,它们将在LinuxPerf中变得可见。
接下来是有趣的内容,我们将使用LinuxPERF事件在操作系统级别上进行分析。重要的是要注意的是PERF命令与Java进程分开运行,因此我们首先需要启动Java,然后启动分析。此外,我们需要分别抓取堆栈信息,以填补空白。在JVM执行内联、类加载、垃圾收集等操作时,请确保分析一个“稳定”的JVM,否则就会读取陈旧的数据,并且某些堆栈帧可能无法得到映射。
为了让我们的生活更轻松,PERF-地图代理工具包括一些用于运行PERF命令,同时获取堆栈映射。甚至还有一个脚本来额外创建火焰图,但是为了清晰起见,我们将手动完成这个步骤。
首先,在启用框架指针信息的情况下启动Java应用程序:
java -XX:+PreserveFramePointer -cp Cpu-0.0.1-SNAPSHOT.jar de.codecentric.training.javaprofiling.cpu.PrimeFinderRunner 1000000 1 1
搜索正在运行的进程的PID(PS aux\grep java)。然后启动分析30秒,然后自动获取堆栈映射信息:
export PERF_RECORD_SECONDS=30
perf-map-agent/bin/perf-java-record-stack <PID>
默认情况下,这将在/tmp。我们还需要PERF事件然而,需要提取的信息如下:
sudo perf script -i /tmp/perf-<PID>.data > /tmp/out-<PID>.stacks
最后,我们可以折叠堆栈信息并一次创建火焰图:
/FlameGraph/stackcollapse-perf.pl /tmp/out-<PID>.stacks | tee /tmp/out-<PID>.collapsed | /FlameGraph/flamegraph.pl --color=java --hash > /tmp/flamegraph.svg
现在您应该看到以下输出:
正如您在创建的映像中所看到的,除了单个正在运行的线程之外,还可以看到更多的信息。这些都是JVM内部运行的操作,比如垃圾收集。在SpringBoot应用程序的第一个示例中,可以找到更多的堆栈。
我邀请你自己安装这个工具并试一试。看看创建了什么样的火焰图,以及如何导航它们以获取所有信息。请参阅下面的一些链接,以使您开始或获得更多的深入信息。