CPU 火焰图基于其流或堆栈跟踪祖先可视化正在运行的代码,显示哪些函数调用了哪些其他函数等等。但是对于 Java,还有另一种可视化相同 CPU 工作负载的方法,它提供了一些额外的见解:Java 包火焰图。这不是可视化堆栈跟踪层次结构,而是可视化 Java 包名称层次结构。我将用一个简单的例子来解释。
这是一个普通的基于堆栈跟踪的Java CPU 火焰图,运行微基准 ( SVG ):
y 轴是堆栈深度。从下到上是父函数到子函数,上边缘显示在 CPU 上运行的函数。
这些火焰图很容易回答许多问题,例如大部分 CPU 时间花在哪里,以及祖先和子函数。 但是有一个问题仍然很棘手:例如在 java/util/* 中花费了多少 CPU 时间? 搜索按钮(右上角)让您通过搜索“java/util”来回答这个问题,右下角将显示 4.3%。 但这包括子函数(故意)。 直接在 java/util 方法中使用多少 CPU 时间,不包括子函数调用? 这需要一些努力才能弄清楚,包括放大每个呼叫并手动排除子呼叫。 包火焰图可以在这里提供帮助。
现在对于相同工作负载的 Java 包火焰图,还显示 CPU 示例 (SVG):
y 轴现在跨越包名称。点击导航。这仅可视化 CPU 上的函数,因此排除了函数祖先。java/util 中的时间是分组在一起的,可以直观的识别:是 3.91%(应该比之前的火焰图少,因为它不包括子调用;不过,这也是一个单独的配置文件,工作量可能有所不同)。似乎有许多细长的矩形:这些不是 Java 方法,因此没有要拆分的包名称。
这个包火焰图比普通的堆栈跟踪火焰图更好吗?当然不。此外,我还使用它作为理解相同 CPU 工作负载的不同视角。
以下是使用我的FlameGraph存储库中的软件制作包火焰图的方法:
#性能记录 -F 99 -a – sleep 30; ./jmaps
#性能脚本 | ./pkgsplit-perf.pl | grep java | ./flamegraph.pl > out.svg
注意到什么了吗?我没有像往常一样将-g与perf record一起使用,所以这不是收集堆栈跟踪。这意味着这种类型的分析具有较低的开销,这是一个好处。这也意味着 Java 不需要与 -XX:+PreserveFramePointer 一起运行,尽管您可能仍然想要收集正常的(堆栈跟踪)火焰图。
此外,某些工作负载可能会破坏 perf 的 127 堆栈帧限制(在 Linux 4.8 及更高版本中可调),这可能会严重破坏正常的火焰图,使其无法读取。包名称火焰图在这种情况下可以正常工作。
我在去年的JavaOne 演讲中介绍了 Java 包火焰图,只是再次使用它们来寻找一些额外的线索。我希望它们对你也有用。
转载翻译自: https://www.brendangregg.com