【Arthas性能排查系列(三)】火焰图分析

时间:2024-10-20 17:38:02
前言

在Java程序中,如果针对单个接口,我们是可以采用trace命令去查看接口的调用连耗时情况的。但是,针对整个项目,不知哪个任务CPU耗时过高的时候,就需要用到火焰图去排查具体问题了

1、首先来看一段简单的代码,这段代码能够让CPU保持相对稳定的运行,并且CPU的占用率较低
object TestMain {

    fun task2() {
        Thread {
            while (true) {
                Thread.sleep(1000)
                println("task2:cpu飙升中~")
            }
        }.start()
    }

    fun task1() {
        Thread {
            while (true) {
                Thread.sleep(1000)
                println("task1:cpu飙升中~")
            }
        }.start()
    }

    fun task3() {
        Thread {
            while (true) {
                Thread.sleep(1000)
                println("task3:cpu飙升中~")
            }
        }.start()
    }

}

fun main() {

    TestMain.task1()
    Thread.sleep(100)
    TestMain.task2()
    Thread.sleep(100)
    TestMain.task3()

}
2、我们使用 thread 命令查看下当前的CPU占用情况

在这里插入图片描述

3、我们来观察一下相对稳定情况下,火焰图的具体情况

涉及命令

profiler start            开始记录火焰图
profiler getSamples       当前采样数
profiler status           当前采样时长
profiler stop             结束记录火焰图

在这里插入图片描述

这里最重要的还是使用 profiler startprofiler stop 命令,用于开始和结束记录火焰图,当结束记录火焰图后,就能获取类似于这样的地址 /Users/abc/Documents/cvte-code/school-group-server/arthas-output/,可以直接通过浏览器查看火焰图


4、我们再来查看一个模拟高CPU占用的场景代码
object TestMain {

    fun task2() {
        Thread {
            while (true) {
                println("task2:cpu飙升中~")
            }
        }.start()
    }

    fun task1() {
        Thread {
            while (true) {
                println("task1:cpu飙升中~")
            }
        }.start()
    }

    fun task3() {
        Thread {
            while (true) {
                println("task3:cpu飙升中~")
            }
        }.start()
    }

}

fun main() {

    TestMain.task1()
    Thread.sleep(100)
    TestMain.task2()
    Thread.sleep(100)
    TestMain.task3()

}
5、使用 thread 命令查看一下CPU占用率,这时候CPU飙高,出现了线程阻塞 BLOCKED

在这里插入图片描述

6、再来看下此时的火焰图

在这里插入图片描述

火焰图分析
图中颜色代表
  • 绿色:Java代码
  • 黄色:JVM,C++代码
  • 红色:用户态,C代码
  • 橙色:内核态,C代码
图中x-y轴代表
  • x轴代表的不是时间,而是采样总量
  • y轴代表方法的调用栈深度,倘若方法调用得越多,火焰越高,顶部的栈就是当前正在执行的方法
栈宽含义(CPU时间)
  • 宽度可以理解为CPU采样率的占比,越宽代表当前栈在采样数中占比高,其可能为三种含义
    • 该函数运行时间长
    • 该函数被调用次数多
平顶现象(一定要格外注意)
  • 平顶现象是由于当前程序的采样数在总采样数中占用过高导致的,出现这种现象需要特意关注一下程序具体的调用栈,采样比例占用率过高,即代表方法在CPU中的占用率过高
总结

火焰图只是用于辅助程序分析定位问题,查看程序在采样期间的大致情况,实际场景还需结合CPU占用率、查看JVM的DUMP快照等方式进行定位