调优利器-火焰图使用图鉴

分布式朝闻道

共 3722字,需浏览 8分钟

 ·

2022-02-25 16:57

本来想起个题目叫 “什么?你还没用过这个工具?” 或者 “再见,火焰图”。但是想了想,己所不欲,勿施于人。

正常写个题目就好了,非搞这么多噱头,就是为了所谓的阅读量和点击量。

如果内容是干货,对人有帮助还好,要是满怀期待打开,进去划拉划拉,越看越不对劲,最后拉到末尾才近乎“上当了”,又是可恶的推广软文,这种感觉就如同吃了翔味的巧克力一样,让人反胃。

言归正传,今天来聊聊性能调优利器,火焰图的安装、使用及分析方法。

火焰图(Flame Graph)是由 Linux 性能优化大师 Brendan Gregg 发明的,和所有其他的 profiling 方法不同的是,火焰图以一个全局的视野来看待时间分布,它从底部往顶部,列出所有可能导致性能瓶颈的调用栈。

先看一个火焰图的样例,看不懂不要紧,后文会解释怎么去理解,稍安勿躁。

fire.PNG

安装

要获得火焰图,需要安装一套组件,核心的组件主要有

  • async-profiler : 性能数据采集
  • FlameGraph     : 性能数据分析,并生成火焰图

接下来按顺序讲解安装过程,读者朋友如果严格按照这个步骤来操作,一般都没有问题。

原料准备

至少需要有一套linux机器,笔者用的是centos-7。

可以搞个阿里云的ecs,也可以搞个虚拟机,当然直接在公司机器上操作也未尝不可,但是要注意安全,root权限下操作要小心(你懂的)。

环境准备

在正式安装之前,先确保环境已经准备好。否则环境搞了半天搞不定,直接放弃,打出GG。

至少保证jdk、perl、c++编译器已经安装完毕。

  1. 安装Java,不用多说了哇,网上教程一大堆。

yum -y install git

  1. 安装GCC编译器

yum install gcc gcc-c++

如果去搜索一下火焰图的其他教程,你可以会发现,有些教程让你安装 perf,我也试过了,装了perf,然后用perf-map-agent去搜集性能数据,直接失败了。

换成async-profiler,直接一次成功。不想踩坑的同学,直接用async-profiler吧,省心。

获取源码

很多教程都说,要安装git,然后使用git clone方式下载源码,但是他们没有交代的是,除了git安装外,你还需要经过配置才可以实现源码下载。

其实源码获取有更简便的方式,或许也不太简便吧,管他呢。

安装lrzsz,用于上传文件

yum install -y lrzsz

安装完成之后,获取async-profiler源码包。

进入async-profiler源码地址,https://github.com/jvm-profiling-tools/async-profiler, 微信禁止外链,读者可以自行复制粘贴至浏览器访问。

download-async.PNG

点击图中红线圈住的 “download zip” ,待下载完成之后,通过 sz -y 方式上传源码包。

upload-async.PNG

使用相同的方式,将FlameGraph的源码包也上传至服务器。

进入FlameGraph源码地址,https://github.com/brendangregg/FlameGraph 下载源码压缩包,也一并上传至服务器。

具体的图不贴了,和上面一模一样。

安装async-profiler

接着就到重头戏了,首先介绍如何安装async-profiler。

进入async-profiler源码上传路径,解压源码(直接unzip async-profiler.zip),进入解压后的文件目录。

  cd async-profiler
  make

make完成之后,等待安装完成即可。

安装FlameGraph

实际上FlameGraph也无需安装,将代码上传并解压,就可以直接使用。

【实战】分析Java性能,生成火焰图

接着讲一个实战案例。

首先,要有一个分析目标程序,我在服务器上部署了一个基于netty的im聊天demo,同时启动服务端与客户端。

启动服务端:

nohup java -jar im-server-1.0.0-jar-with-dependencies.jar > Log.log 2>&1 &

启动客户端,指定服务端地址(客户端与服务端在同一个机器)

java -jar  -Dremote_ip=127.0.0.1 im-client-1.0.0-jar-with-dependencies.jar

启动性能分析

启动性能分析,持续收集一分钟服务端性能指标。

先获取服务端的PID:

 
[root@snowalker ~]# ps -ef | grep java
root      9349     1  0 Feb11 ?        00:09:40 java -jar im-server-1.0.0-jar-with-dependencies.jar

PID为9349,接着启动async-profiler收集java进程的性能指标。

./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

pid为进程实际的进程id,这里就是9349,那么只需要执行命令:


./profiler.sh -d 60 -o collapsed -f /tmp/test_02.txt 9349

运行期间,处于阻塞状态,直到约定时间完成。

运行期间,接着模拟用户聊天,连续发送消息至服务端:

send-message.PNG

性能数据收集结束之后,到/tmp/ 查看输出的性能指标文件:


[root@snowalker tmp]# ll
-rw-r--r-- 1 root root   6008 Feb 22 12:45 test_02.txt

可以看到,性能指标已经收集完成,接着就到火焰图生成工具-FlameGraph的出场时间了。

生成火焰图

执行命令

perl flamegraph.pl --colors=java /tmp/test_02.txt > /tmp/test_02.svg

查看tmp路径下文件:


[root@snowalker tmp]# ll
-rw-r--r-- 1 root root  32452 Feb 22 12:46 test_02.svg
-rw-r--r-- 1 root root   6008 Feb 22 12:45 test_02.txt

可以看到已经生成一张svg格式的图片,下载图片到本地:

sz test_02.svg

使用浏览器打开图片,一张美观的火焰图展现在面前:

fire.png

如何阅读火焰图?

有了火焰图,我们得读懂它才能利用它进行性能优化。

一眼看过去,红红火火的,密密麻麻,可能你觉得案例中 不够密密麻麻,如果分析的是线上的程序,那复杂程度足够让人眼花缭乱。

那么我们应该如何理解火焰图的内容呢?

简单的说:

  1. 火焰图,每一列代表一个调用栈,每一个格子代表一个函数
  2. 纵轴,即垂直方向的y轴,展示了栈的深度,按照调用关系从下到上排列。最顶上格子代表采样时,正在占用 cpu 的函数。
  3. 横轴,即水平方向的x轴,表示:火焰图将采集的多个调用栈信息,通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间。【横轴没有特殊的含义,不代表调用关系!】
  4. 横轴格子的 宽度 代表其在采样中出现频率,所以一个格子的宽度越大,说明它是瓶颈原因的可能性就越大。
  5. 火焰图格子的颜色是随机的暖色调,方便区分各个调用信息。
  6. 其他的采样方式也可以使用火焰图, on-cpu 火焰图横轴是指 cpu 占用时间,off-cpu 火焰图横轴则代表阻塞时间。
  7. 采样方式可以是单线程、多线程、多进程甚至是多 host,进阶用法参考文档:https://www.brendangregg.com/flamegraphs.html

另外,多说两句,火焰图的栈深度与y轴高度成正比,可以这么认为:造成性能问题的基本都处于调用栈的栈顶位置。

因为栈顶位置的性能问题会间接拖慢整个调用栈,简单的举个例子:方法A调用方法B,方法B调用方法C。

如果方法C执行的慢则间接导致方法B慢,从而导致方法A慢。符合我们说的通过分析栈顶从而达到分析瓶颈的目的。

如果A方法本身就慢呢?通过火焰图也是可以看出来的,这种底层栈的宽度很宽,但是建立在其撒花姑娘的调用链线条都很窄,火焰图呈现“┻”型,那么我们基本可以确定,栈底方法本身就存在性能问题。

我们的一个分析火焰图的基本原则就是,从栈顶看起,往栈底分析。


浏览 152
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报