来源:https://zhenbianshu.github.io
前言
工具的进化一直是人类生产力进步的标志,合理使用工具能大大提高我们的工作效率,遇到问题时,合理使用工具更能加快问题排查的进度。这也是我为什么非常喜欢 shell 的原因,它丰富的命令行工具集加管道特性处理起文本数据集来真的精准而优雅,让人迷醉。
但很多时候文本的表现力非常有限,可以说匮乏,表达绝对值时,自然是无往不利,但在展示相对值时,就有些捉襟见肘了,就更不用说多维数据了。
我们用 shell 可以非常快速地查询出文本内的累加值、最大值等,但一遇到两组值的相关性分析时,就束手无策了。这个时候,就需要使用另一种分析工具 – 图
了,如散点图就能很清晰地展示相关性。
今天就准备介绍一种图,火焰图
,之前组内大神分享过它的使用办法,但我之后很久都没有用过,以至于对它没有什么深刻印象,最近排查我们 Java 应用负载问题时试用了一下,这才对它的用途有了点心得。
介绍
引子
在排查性能问题时,我们通常会把线程栈 dump 出来,然后使用 grep --no-group-separator -A 1 java.lang.Thread.State jstack.log | awk 'NR%2==0' | sort | uniq -c | sort -nr
类似的 shell 语句,查看大多数线程栈都在干什么。而由线程栈的出现频率,来推断 JVM 内耗时最多的调用。
至于其原理,设想广场上有一个大屏幕在不停地播放各种广告。如果我们随机对大屏幕拍照,次数多了,统计照片中各个广告出现的频率,基本可以得出每个广告的播放时长占比了。搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
2444 at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1200)
1587 at sun.misc.Unsafe.park(Native Method)
795 at java.security.Provider.getService(Provider.java:1035)
293 at java.lang.Object.wait(Native Method)
292 at java.lang.Thread.sleep(Native Method)
73 at org.apache.logging.log4j.core.layout.TextEncoderHelper.copyDataToDestination(TextEncoderHelper.java:61)
71 at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
70 at java.lang.Class.forName0(Native Method)
54 at org.apache.logging.log4j.core.appender.rolling.RollingFileManager.checkRollover(RollingFileManager.java:217)
但是这样有些问题,首先写 shell 挺费事的,另外如果我想查看自栈顶第二个栈的最多调用,即使修改了 shell 命令,结果也不直观。
产生这个问题的主要原因是,我们的线程栈是有调用关系的,即我们需要考虑线程栈的 调用链
和 出现频率
两个维度,而单一的文本表现这两种维度比较困难,所以,著名性能分析大师 brendan gregg 就提出了火焰图。
介绍
火焰图,因其形似火焰而得名,其开源代码地址:
当我们点击方块时,图片会从我们点击的方块为基底向上展开,而我们鼠标指向方块时,会展示出方块的详细说明。
特性
分析
由上文中的火焰图特性特性,查看火焰图时,我们最主要的关注点要放在方块的宽度上,因为宽度代表了调用栈在全局出现的次数,次数代表着出现频率,而频率也就可以说明耗时。
但是观察火焰图底部或中部方块的宽度占比意义不大,如上面的火焰图,中部的 do_redirections
函数宽度是 24.87%,也就是说它耗用了整个应用近四分之一的时间,但是真正消耗时间的并不是 do_redirections 函数,而是 do_redirections 内部又调用的其他函数,而它的子调用分为了很多个,每个调用的耗时并没有异常。搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
我们更应该关注的是火焰图顶部的一些 “平顶山”,顶部说明它没有子调用,方块宽说明它耗时长,长时间 hang 住,或者被非常频率地调用,这种方块指向的调用才是性能问题的罪魁祸首。
找到了异常调用,直接优化它,或者再根据火焰图的调用链层层向下,找到我们的业务代码进行优化,也就大功告成。
应用场景
每种工具都有其适合的应用场景,火焰图则适合用在:
实现
生成工具
brendan gregg 大神已经把生成火焰图的方法用 perl 实现了,开源代码就在上文的 Github 仓库中,根目录下的 flamegraph.pl
文件就是可执行的 perl 文件了。
这个命令还可以传入各种参数,支持我们修改火焰图的颜色、大小等 。另外,JVM 系列面试题和答案全部整理好了,微信搜互联网架构师,在后台发送:2T,可以在线阅读。
但 flamegraph.pl 只能处理特定格式的文件,像:
a;b;c 12
a;d 3
b;c 3
z;d 5
a;c;e 3
前面是调用链,每个调用之间用 ;
隔开,每行后面的数字是调用栈出现的次数。
如上面的数据,用 flamegraph.pl 生成的火焰图如下图:
数据准备
至于我们的 jstack 信息如何被处理成上面的格式,大神则为常见的 dump 格式都提供了工具,像 stackcollapse-perf.pl
可以处理 perf
命令的输出,stackcollapse-jstack.pl
处理 jstack
输出,stackcollapse-gdb.pl
处理 gdb 输出的栈等。
也可以用 shell 简单地实现一下 jstack 的处理方式:
grep -v -P '.+prio=d+ os_prio=d+' | grep -v -E 'locked <' | awk '{if ($0==""){print $0}else{printf"%s;",$0}}' | sort | uniq -c | awk '{a=$1;$1="";print $0,a}'
小结
做开发越久,越能感受得到工具的重要性,所以我准备加一个专题来专门介绍我使用的各种工具。当然,这也就更需要我更多地了解、使用和总结新的工具了。
感谢您的阅读,也欢迎您发表关于这篇文章的任何建议,关注我,技术不迷茫!小编到你上高速。
· END ·
最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全。
正文结束
推荐阅读 ↓↓↓
1.不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事
5.37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...
6.IntelliJ IDEA 2019.3 首个最新访问版本发布,新特性抢先看