一款性能调优利器 — 火焰图
时间:2021-11-08 15:42:00
手机看文章
扫描二维码
随时随地手机看文章
[导读]来源:https://zhenbianshu.github.io/2019/04/application_debug_tools_flamegraph.html|前言工具的进化一直是人类生产力进步的标志,合理使用工具能大大提高我们的工作效率,遇到问题时,合理使用工具更能加快问题排...
| 前言
工具的进化一直是人类生产力进步的标志,合理使用工具能大大提高我们的工作效率,遇到问题时,合理使用工具更能加快问题排查的进度。这也是我为什么非常喜欢 shell 的原因,它丰富的命令行工具集加管道特性处理起文本数据集来真的精准而优雅,让人迷醉。
| 介绍
引子
在排查性能问题时,我们通常会把线程栈 dump 出来,然后使用grep --no-group-separator -A 1 java.lang.Thread.State jstack.log | awk 'NR%2==0' | sort | uniq -c | sort -nr类似的 shell 语句,查看大多数线程栈都在干什么。而由线程栈的出现频率,来推断 JVM 内耗时最多的调用。
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 命令,结果也不直观。
介绍
火焰图,因其形似火焰而得名,其开源代码地址:
https://github.com/brendangregg/FlameGraph它是一种 svg 可交互式图形,我们通过点击和鼠标指向可以展示出更多的信息。下图就是一个典型的火焰图,从结构上,它是由多个大小和颜色各异的方块构成,每个方块上都有字符,它们底部连接在一块,组成火焰的基底,顶部分出许多”小火苗”。
特性
介绍火焰图的分析前,我们要首先说明它的特性:
- 由底部到顶部可以追溯一个唯一的调用链,下面的方块是上面方块的父调用。
- 同一父调用的方块从左到右以字母序排列。
- 方块上的字符表示一个调用名称,括号内是火焰图指向的调用在火焰图中出现的次数和这个方块占最底层方块的宽度百分比。
- 方块的颜色没有实际意义,相邻方块的颜色差只为了便于查看。
分析
那么,给我们一张火焰图,我们怎么能看出系统哪里有问题呢?
应用场景
每种工具都有其适合的应用场景,火焰图则适合用在:
- 代码循环分析:如果代码中有很大的循环或死循环代码,那么从火焰图的顶部或接近项部的地方会有很明显的”平顶”,表示代码频繁地在某个线程栈上下切换。但需要注意的是,如果循环的总耗时不长,在火焰图上不会很明显。
- IO 瓶颈/锁分析:在我们的应用代码中,我们的调用普遍都是同步的,也就是说在进行网络调用、文件 I/O 操作或未成功获得锁时,线程会停留在某个调用上等待 I/O 响应或锁,如果这个等待非常耗时,会导致线程在某个调用上一直 hang 住,这在火焰图上表现得会非常清晰。与此相对的是,我们应用线程构成的火焰图无法准确地表达 CPU 的消耗,因为应用线程内没有系统的调用栈,在应用线程栈 hang 住时,CPU 可能去做其他事了,导致我们看到耗时很长,而 CPU 却很闲。
- 火焰图倒置分析全局代码:火焰图倒置有时也会很实用,如果我们的代码 N 个不同的分支都调用某一方法,倒置后,所有栈顶相同的调用被合并在一块,我们就能看出这个方法的总耗时,也就很容易评估出优化这个方法的收益。
| 实现
既然火焰图这么强大,那么我们该怎么实现呢?
生成工具
brendan gregg 大神已经把生成火焰图的方法用 perl 实现了,开源代码就在上文的 Github 仓库中,根目录下的flamegraph.pl文件就是可执行的 perl 文件了。
a;d 3
b;c 3
z;d 5
a;c;e 3
前面是调用链,每个调用之间用;隔开,每行后面的数字是调用栈出现的次数。
数据准备
至于我们的 jstack 信息如何被处理成上面的格式,大神则为常见的 dump 格式都提供了工具,像stackcollapse-perf.pl可以处理perf命令的输出,stackcollapse-jstack.pl处理jstack输出,stackcollapse-gdb.pl处理 gdb 输出的栈等。
| 小结
火焰图总结完了,以后再遇到性能问题又多了一种应对方式。