JAVA性能分析之使用火焰图

随着业务的发展,使用接口提供的服务的业务越来越多,不同的业务对调用耗时的要求不同,当然,耗时越少越好。而近来已经有三四个调用方反映接口调用耗时太不稳定,代码上的优化已经做了很多,有效果,但还不够。同时,压力测试显示,复杂业务在调用量单机3000次/分时CPU就已经100%了,与预期差距太远。为了改善接口性能,找到性能瓶颈,不得已祭出了大杀器————火焰图。
网上的关于java火焰图的讲解大部分来自于Brendan Gregg的博客,火焰图的效果大致如下图:
最终生成一个SVG格式的图片,图上的每个色块代表的是一个线程栈帧,纵轴方向从下往上表示的是函数调用栈,而横轴方向色块的宽度表示的是函数调用占用CPU的比例。在使用Profiler对CPU进行采样时,根据CPU当前执行所处栈位置以及各个函数栈在总的采样次数所占比例就可以得出各个函数执行时的CPU占用比例。

火焰图生成方式

火焰图的生成过程有两步:
1. 使用Profiler工具生成trace文件;
2. 将trace文件转换为svg格式的火焰图文件;
Brendan使用的Profiler是lightweight-java-profiler,这个需要自己编译:
make BITS=64 all
还可以通过更改global.h文件来配置采样次数:

// 每秒采样次数
static const int kNumInterrupts = 100;
// Maximum number of stack traces
static const int kMaxStackTraces = 3000;
// 采样栈深度
static const int kMaxFramesToCapture = 128;

在长时间采样时,可以适当地减少每秒采样次数,不然最终生成的文件会很大,分析起来比较麻烦。
编译生成文件liblagent.so,使用起来很简单,在java启动参数添加如下内容:
-agentpath:path/to/liblagent.so
java程序启动后会在当前目录生成一个traces.txt文件,但文件中只有一些说明信息。程序正常结束(不是通过kill -9杀掉进程)后,才会写入具体采样信息。
Brendan提供了一个工具集来将各种Profiler生成的文件转化为火焰图svg格式。使用方式如下:

git clone http://github.com/brendangregg/FlameGraph
cd FlameGraph
./stackcollapse-ljp.awk < ../traces.txt | ./flamegraph.pl > ../traces.svg

除了使用lightweight-java-profiler作为Profiler外,还有其他的选择,比如honest-profiler,lightweight-java-profiler会从java虚拟机启动开始采样,而有时候我们需要在CPU飙高的时候开始,这时候honest-profiler提供的动态启停功能就有用武之地了。
使用honest-profiler生成火焰图:
首先,需要先下载或者手动编译honest-profiler,使用方式如下:

java -agentpath:/path/to/location/liblagent.so=interval=7,logPath=/path/to/output/log.hpl,start=0,host=127.0.0.1,port=12345 <normal-java-commandline>

其中interval是采样频率,logPath表示生成的采样文件路径,start可取0或1,表示是否启动时就开启采样,host和port表示监听控制的IP地址和端口号。
启动成功后,通过telnet连接到127.0.0.1:12345,即可通过命令控制采样的开始和结束。支持的命令包括:

  • start,开始采样
  • stop,停止采样
  • status,打印当前采样状态和采样文件目录
  • get [ParamName],获取参数值
  • set [ParamName] [ParamValue1] ….[ParamValueN] 设置参数值
    [ParamName]可以是intervalMin, intervalMax, interval, maxFrames 或者 logPath

在生成log.hpl文件后,还需要通过其他工具才能转换为log.svg文件,当时是通过开源工具hprof2flamegraph执行的这个过程。使用步骤如下:

git clone https://github.com/chengxiayan/hprof2flamegraph.git
cd hprof2flamegraph
python stackcollapse_hpl.py --discard-lineno --discard-thread log.hpl>log.hpl.output
./flamegraph.pl log.hpl.output>log.svg

其中python版本需要2.7以上,windows上安装了ActivePerl以后也可以通过执行

perl ./flamegraph.pl log.hpl.output>log.svg

来完成第二步转换过程。
关于采样工具的选取,可以看看文章 Evaluating the Accuracy of Java Profilers,这里面列举了xprof,hprof,jprofile和yourkit四种采样器,并通过几个压测场景证明了这几种采样器的结果是相互矛盾的。总结的原因有三点:
1. 采样器采样点不够随机,这几种采样器都只有在safe point采样;
2. 不同的采样器会注入不同的代码,从而影响程序优化过程,同时也影响了safe point的分布,进一步造成采样差异;
honest-profiler号称是避开了通过SUN/Oracle management agent去采样堆栈,而是使用自己实现的使用UNIX 操作系统信号和为Oracle Performance Studio 设计的内部API的sampling agent,从而提升了采样准确率。

没有更多推荐了,返回首页