java性能火焰图的生成
一、前言
开始之前,你需要准备的环境:
Linux系统机器或者虚拟机一台,里面需要安装的软件:git
、jdk
、perl
。
二、简单介绍
java性能分析火焰图的所做的事情就是能够分析出java程序运行期间存在的性能问题,因为某段代码拖慢整个程序执行是不允许的,因此靠火焰图的绘制和分析就可以找出类似的“问题代码段”。
那么这个图是怎么来的呢?首先跟大多数监控系统一样,数据采集+前端绘图,这个图也是根据某些数据绘制而成的,绘图工具本篇文章采用FlameGraph,而负责收集这些数据的工具,本篇采用async-profiler,这个工具会在程序运行期间向jvm
发送信号采集其运行期数据(简单来说就是通过该工具可以找出程序中占用CPU资源时间最长的代码块,这里async-profiler
的实现使用了jvmti
,戳这里简单了解一下),然后生成相应的数据格式文件,而FlameGraph则负责读取和解析数据文件生成对应的火焰图(svg
文件)。
三、使用&安装
3.1:环境搭建
确认你的机器已经安装了git、jdk、perl、c++编译器,部分可参考:安装杂记
3.2:clone相关项目
下载下来所需要的两个项目(这里建议放到data目录下):
1 | git clone https://github.com/jvm-profiling-tools/async-profiler |
3.3:编译
下载好以后,需要打开async-profiler
文件,输入make
指令进行编译:
1 | cd async-profiler |
3.4:编写测试程序
编译完成后,我们来写一个简单的java程序:
1 | public class Test { |
非常简单的一个java类,main方法里所做的事情也很简单,现在把这个文件搞到data目录下,javac命令
编译,java命令
启动。
然后找到这个java程序的进程id:
1 | ps -ef | grep java |
可以确认此时Test类运行时的java进程pid = 30937
,当然也可以使用jps
命令直接查看java进程,效果是一样的。
3.5:生成火焰图数据
ok,上述步骤完成后,现在进入async-profiler
那个项目的目录下,然后输入如下指令:
1 | ./profiler.sh -d 60 -o collapsed -f /tmp/test_01.txt ${pid} |
上面的-d
表示的是持续时长,后面60代表持续采集时间60s,-o
表示的是采集规范,这里用的是collapsed
,-f
后面的路径,表示的是数据采集后生成的数据存放的文件路径(这里放在了/tmp/test_01.txt
),${pid}
表示的是采集目标进程的pid,也就是上面提到的30937
回车运行,运行期间阻塞,知道约定时间完成。运行完成后,现在去tmp下看看有没有对应文件:
3.6:生成svg文件
上一步产生的文件里的内容,肉眼是很难看懂的,所以现在FlameGraph的作用就体现出来了,它可以读取该文件并生成直观的火焰图,现在进入该项目目录下面,执行如下语句:
1 | perl flamegraph.pl --colors=java /tmp/test_01.txt > test_01.svg |
因为是perl
文件,这里使用perl指令
运行该文件,后面--colors
表示着色风格,这里是java,后面的是数据文件的路径,这里是刚刚上面生成的那个文件/tmp/test_01.txt
,最后的test_01.svg
就是最终生成的火焰图文件存放的路径和文件命名,这里是命名为test_01.svg
并保存在当前路径,运行后看到该文件已经存在于当前目录下:
3.7:展示
现在下载下来该文件,使用浏览器打开,效果如下:
不太好看懂,上线后分析下。
续更
续更,公司内部火焰图已经上线,通过更为复杂的业务场景生成的图反而看起来更容易理解一些,因为业务代码的调用也会打印出来,下面贴一下内部某业务系统火焰图:
这张图是在某个业务系统运行时,采样60s生成的火焰图,通过这样一张图可以看出,x轴
为调用顺序,y轴
为栈深,线条颜色无实际意义(并不是越红性能越差之类的),线条长度代表CPU执行该方法时所花费的时间占比,一般来说需要关注的就是栈顶,且宽度比较大的那个。因为一般处于栈顶的,而且宽度比较大的调用栈,说明其存在性能问题,这样分析的原因如下:
栈深度与y轴高度成正比,一般造成性能问题的都在调用栈的栈顶位置,因为栈顶位置的性能问题会间接拖慢整个调用栈,比如上图中每个栈底的线条都很长,这是因为越往上栈越深,对下层的影响就越大,可以简单抽象成方法调用:A调用B,B调用C,C慢会间接导致B慢,从而导致A慢,当然符合这种情况就适合之前说的看栈顶分析瓶颈的方法,如果A本身就慢呢?通过火焰图也是可以看出来的,比如栈底的线条宽度很宽,但是建立在该栈底的调用链上,线条都很窄,火焰图呈现 ┻ 型,那么就可以认定,栈底方法存在性能问题,一般情况下都是从栈顶看起,视情况而定~