在我的前一天转的一篇文章中([转]go's march to low latency gc),Twitch的Rhys Hiltner使用火焰图(FlameGraph)来分析Go程序的性能给我留下了深刻的印象,它使用Brendan Gregg创建的火焰图工具生成直观的图像,很方便的分析Go的各个方法占用的CPU的时间。
因为我的一部分时间还是使用Java开发,所以就想到有没有Java相关的工具生成火焰图呢?答案当然是肯定的,而且它更早的应用于Java程序的性能分析。
火焰图工具的作者Brendan Gregg专门写了一篇文章:Java Flame Graphs,介绍如何生成火焰图的。这就是本文要翻译的文章。
Netflix深度使用了火焰图工具,他们专门写了一篇文章Java in Flames,介绍他们是如何使用的,而且前段时间他们又写了一篇文章: 如何使用火焰图分析性能,每天为Netflix节省一千三百万分钟的计算时间,文中介绍了他们如何使用火焰题找到耗时的问题所在,为公司节省了大量的时间和金钱。(Brendan Gregg就是Netflix的员工,他的站点觉得是一个值得阅读的地方,还有很多其它性能相关的专题,比如Linux性能工具 http://www.brendangregg.com/linuxperf.html)
既然火焰图这么有效,你难道不想了解一下吗?
以下是Brendan Gregg文章的翻译。
Java火焰图是一个新的方法来可视化CPU的使用情况,本文中我会展示如何使用免费开源的工具来创建它:Google的lightweight-java-profiler 以及我的火焰图工具。希望有一天火焰图能成为所有的Java Profiler工具的标准特性,就像现在的调用图(call tree graph)一样。
更新信息(2015年8月) Netflix的博客的文章Java in Flames展示了最新的最好的产生Java火焰图的方法,使用 Linux perf_events来一起显示Java和系统代码(system code paths)。我也在Java CPU Flame Graphs一文中做了介绍。本文仅分析Java相关的方法,包含一些这些链接中的警告(caveats)。
下图显示了使用vert.x实现的一个简单的JavaScript的大图:
惊艳! 鼠标放上去还能看细节。它是SVG格式的。
Y轴是栈的深度(stack depth),X轴是所有的采样点的集合,每个方框代表一个栈帧(stack frame)。 颜色没有意义,只是随机的选取的。左右顺序也不重要。
你可以从最宽的帧看起,从底往上看,帧上的分叉代表不同的代码路径。我的文章CPU flame graphs有很详细的解释,这个PPTflame graphs presentation 中业余介绍。一旦你掌握这些窍门,你可以快速识别和量化的CPU使用率。
左边最高的高是Mozilla Rhino JavaScript engine,它吃掉了 42.70% CPU (可以查看最低部的mozilla frame; 百分比包含它上面所有的子帧)。Java/vert.x 不需要运行这个引擎,释放这些CPU资源的话,可以增加大约一倍的性能。
其它的细节也很有趣:其它最大的CPU占用是write0()
, 大约31.99%, 这是可取的 – vert.x要响应请求,发送response。处理调优write0,要想提升性能,就得检查和减少其它代码的CPU占用。移除Rhino是最好的提升方法。
通过收集不同天的或者不同版本的火焰图,你可以快速地比较它们的性能,提供量化的数据。
Profile 数据收集
火焰图可以将采样的正在CPU上运行的栈信息可视化。你可以使用任意的可提供给你stack trace的profiler工具,我最开始使用hprof进行采样,但是发现它有些问题。
在这个例子中我使用Jeremy Manson开发的lightweight-java-profiler 。它是一个精确的profiling技术的开源演示版本,并不是一个产品级的产品,所以需要一些手工操作,以下是我的步骤:
1、下载软件
|
|
2、修改Makefile
我使用vi编辑Makefile,设置BITS为64, 并且为我的系统增加include路径`。 我的改变如下:
|
|
3、编译软件
|
|
4、设置agent
在运行Java程序的时候我加上了下面的设置:
|
|
Java开始运行时采样开始,程序结束时采样结束,采样数据会写入到traces.txt文件中,类似hprof的格式。火焰图工具会读取这个文件。
当然运行这个采样会有些开销,对于我的程序来说可以忽略不计(~1% 请求速率的降低,7%CPU的增长), 但是你的情况可能会不同。稍后会介绍如何检查采样速率。
火焰图
现在将traces.txt处理成火焰图:
|
|
stackcollapse-ljp.awk
是一个awk脚本,可以转换lightweight-java-profiler的输出为火焰图工具所需的格式(每栈一行)。 flamegraph.pl
脚本有一些选项设置(-h查看选项列表),可以定制输出,包括改变标题。
现在可以在浏览器中打开traces.svg文件。
定制profiler
值得注意的是你可以配置lightweight-java-profiler的一些选项,打开src/globals.h文件:
|
|
我可能倾向将采样速率从每秒100次降到50次,甚至更低,尤其是当我想长时间搜集数据的时候。我也会增大最大栈帧数,这样火焰图就能显示全栈信息。
Richard Warburton也写了一个profiler: honest-profiler,也是建立在相同的全精确性能分析技术之上(accurate profiling technique),就像lightweight-java-profiler一样,你也需要自己编译才能使用。
这些profiler只是查看Java内部的性能,我理想的profiler应该包含用户栈信息和内核栈信息,这能让我们集可以分析JVM GC的代码路径,也可以分析其它的JVM内部的性能以及内核的性能。
火焰图已经在其它领域证明了它的用处,包括内核的性能,Node.JS, Ruby, Perl等等, 可以查看我的更新章节以了解最新的信息。
参考文档
以下是译者增加的其它参考资料:
- http://www.brendangregg.com
- http://techblog.netflix.com/2015/07/java-in-flames.html
- http://techblog.netflix.com/2016/04/saving-13-million-computational-minutes.html
- http://www.infoq.com/cn/news/2015/08/java-flamegraph
- http://www.brendangregg.com/FlameGraphs/cpuflamegraphs.html
- https://github.com/brendangregg/FlameGraph
- http://isuru-perera.blogspot.com/2015/05/flame-graphs-with-java-flight-recordings.html
- http://isuru-perera.blogspot.com/2015/09/java-mixed-mode-flame-graphs.html
- https://www.infoq.com/news/2015/08/JVM-Option-mixed-mode-profiles