通过火焰图定位性能瓶颈

当您的线上应用出现CPU、内存资源使用率高或大量慢调用问题时,可以通过ARMS持续剖析功能生成的火焰图对其进行根因定位。本文介绍如何通过持续剖析功能的火焰图定位性能瓶颈。

什么是火焰图

火焰图(Flame Graph)是一种可视化程序性能分析工具,它可以帮助开发人员追踪程序的函数调用以及调用所占用的时间,并展示对应信息。

f13b95a2436706e37974aad93e9e0a40

其核心思想是将程序的函数调用方法栈转化为一个矩形的火焰形图像,每个矩形的宽度表示该函数对应资源使用占比,高度表示函数整体的调用深度。通过对比不同时间点的火焰图,可以快速诊断程序的性能瓶颈所在,从而有针对性地进行优化。

火焰图类型

广义上的火焰图画法分为2种,分别是函数方法栈栈底元素在底部,栈顶元素在顶部的狭义火焰图,如下图1所示;以及方法栈栈底元素在顶部,栈顶元素在底部的冰柱状火焰图,如下图2所示。

1.狭义火焰图

f13b95a2436706e37974aad93e9e0a40

2.冰柱状火焰图

image

使用火焰图

火焰图作为性能分析的可视化技术,只有理解它才能基于其做性能分析。例如,对于一张CPU热点火焰图,通过查看火焰图中是否有较宽的栈顶,即可了解CPU中是否存在耗时较长的函数。

因为火焰图所绘制的内容就是计算机中方法执行的方法栈。而计算机中函数的调用上下文是基于一个叫做栈的数据结构去存储,栈数据结构的特点是元素先进后出,因此栈底就是初始调用函数,依次向上就是一层层的被调用的子函数。当最后一个子函数也就是栈顶执行结束后才会依次从上往下出栈,因此栈顶较宽,就表示该子函数执行时间长,其下方的父函数也会因其一直执行无法即时出栈而导致最终整体耗时较长,具体过程如下图所示:

image

分析火焰图的方法步骤如下:

  1. 判断火焰图对应的类型,找到其中的栈顶方向。

  2. 如果火焰图总资源占用高,就继续检查火焰图的栈顶是否有较宽的部分。

  3. 如果存在较宽的栈顶,沿着栈顶依次往栈底方向搜索,找到第一个包名为所分析应用自身定义的方法行,然后重点排查该方法是否存在优化空间。

实践分析

ARMS持续剖析功能通过火焰图可视化技术呈现应用的性能剖析数据,当线上应用出现CPU、内存资源使用率高或大量慢调用问题时,可以通过该功能生成的火焰图对其进行根因定位,从而协助开发者优化程序、降低延迟、增加吞吐、节约成本。如果还未接入持续剖析功能,请参考接入持续剖析功能完成接入。

下图为一张资源占用较高的火焰图,火焰图中性能瓶颈的具体分析步骤如下:

image

  1. 通过火焰形状可以判断这是一张栈底在上,栈顶在下的冰柱状火焰图,因此需要从下往上分析。

  2. 分析下方的栈顶,可以发现右侧较宽的栈顶为java.util.LinkedList.node(int)方法。

  3. 由于该栈顶是JDK中的库函数,并非为业务方法,因此,沿着栈顶方法java.util.LinkedList.node(int)从下往上搜索,依次经过java.util.LinkedList.get(int) > com.alibaba.cloud.pressure.memory.HotSpotAction.readFile(),而com.alibaba.cloud.pressure.memory.HotSpotAction.readFile()属于所分析应用的业务方法,即为第一个所分析应用自身定义的方法行,其耗时为3.89s,占整张火焰图的76.06%,因此com.alibaba.cloud.pressure.memory.HotSpotAction.readFile()是该火焰图所采集时段内资源占用较高的显著瓶颈所在,可以根据该方法名,对业务中相关方法的逻辑进行梳理,查看是否存在优化空间。

    另外也可以根据上述分析方法对火焰图左下角java.net.SocketInputStream的相关方法进行相同分析,可以发现其第一个属于所分析应用自身定义的父方法为com.alibaba.cloud.pressure.memory.HotSpotAction.invokeAPI,总占比约为23%。