使用Arthas生成火焰图分析性能瓶颈

摘要:通过Java应用诊断利器Arthas生成火焰图并快速诊断系统性能瓶颈。当你握紧Arthas这把手术刀,每一次线上危机都是展示技术深度的舞台。

综述

  有次被借调到其它项目组负责重大版本迭代,涉及改动点非常多,开发完成后为了查看系统性能而进行压测,但是压测报告显示存在Full GC问题,由于欠缺解决Full GC问题的快速响应能力,被项目组负责人百般刁难,想着法的PUA。痛定思痛,亡羊补牢,故总结了如何用Arthas(阿尔萨斯)快速诊断线上如下四个常见问题:

  1. 不知道哪儿慢:即不知道哪个函数占用大量 CPU 时间。
  2. 不知道谁是瓶颈:即不知道哪个方法被频繁调用导致性能问题,如存在锁竞争的热点函数大概率是瓶颈。
  3. 不知道为何输出结果不符合预期。
  4. 不知道系统应用为什么出现FULL GC。

  希望本文对刚刚接触系统慢、系统卡或者Full GC问题的读者有所帮助。相对于传统工具,Arthas 有三大降维打击优势:

  • 代码无侵入诊断:不需要在程序中进行额外配置,更不需要手动埋点。
  • 功能强大:Arthas 提供了四十多种命令,从查看线程调用链,到查看输入输出,再到反编译代码等,应有尽有。
  • 占用CPU低:Arthas占用CPU百分比低,而CPU使用率飙高时根本打不开JProfiler。

  攥指成拳固然能聚合力,但也需要每根指头都发挥作用,每个部分都找准位置。使用 Arthas 生成火焰图可以帮助我们全方位快速诊断 Java 应用的性能瓶颈,精细化扫描 CPU 热点代码的位置。下面介绍生成火焰图的详细步骤,假如需要了解更多Arthas信息,请移步官方文档传送门

判断是否已安装 Arthas

  运行以下命令查看 Arthas 版本:

$ java -jar arthas-boot.jar --version
Error: Unable to access jarfile arthas-boot.jar

  从上述执行结果得知返回的是 Unable to access jarfile,而不是版本号,表示没有安装 Arthas,故需要安装;否则,直接运行即可。返回版本号时,终端执行结果如下:

$ java -jar arthas-boot.jar --version
[INFO] JAVA_HOME: /opt/java/openjdk/jre
[INFO] arthas-boot version: 4.0.5
Local versions:
Remote versions:
 4.0.5
 ...

下载和安装 Arthas

  如果还没有安装 Arthas,可以通过以下命令下载,分分钟接入生产环境:

$ curl -O https://alibaba.github.io/arthas/arthas-boot.jar

  Arthas是一个jar包,下载后直接在根目录下运行java -jar arthas-boot.jar命令启动已经安装的Arthas:

$ java -jar arthas-boot.jar
[INFO] JAVA_HOME: /opt/java/openjdk/jre
[INFO] arthas-boot version: 4.0.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 1 /com.example.MyApp-0.0.1-SNAPSHOT.jar

  Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER的意思是arthas可以自动识别Java进程,我们在选择进程号后单击回车键即可。

选择目标进程

  Arthas启动后会自动扫描当前运行的 Java 进程,输入对应的进程编号并单击回车键即可选中你要分析的应用。输入进程号 1 并回车就是选择目标 com.example.MyApp。此时此刻就会安装arthas:

// 首次启动时,需要下载arthas,再次启动就没有downloading提示了
[INFO] File size: 13.75 MB, downloaded size: 13.70 MB, downloading ...
[INFO] Download arthas success.
[INFO] arthas home: /root/.arthas/lib/4.0.5/arthas
[INFO] Try to attach process 1
Picked up JAVA_TOOL_OPTIONS: 
[INFO] Attach process 1 success.
[INFO] arthas-client connect 127.0.0.1 3658
  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---.                           
 /  O  \ |  .--. ''--.  .--'|  '--'  | /  O  \ '   .-'                          
|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.                          
|  | |  ||  |\  \    |  |   |  |  |  ||  | |  |.-'    |                         
`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'                          

wiki        https://arthas.aliyun.com/doc                                       
tutorials   https://arthas.aliyun.com/doc/arthas-tutorials.html                 
version     4.0.5                                                               
main_class  /com.example.MyApp-0.0.1-SNAPSHOT.jar -Dfile.encoding=utf-8 
             --spring.profiles.active=test                                      
pid         1                                                                   
start_time  2025-08-04 02:17:15.480                                             
currnt_time 2025-08-04 03:07:06.750                                             

[arthas@1]$

使用profiler生成火焰图

  在一个Java应用中使用 Arthas 的 profiler 命令生成火焰图(Flame Graph)。执行Arthas 的 profiler 命令可以生成火焰图,支持 CPU 和内存分析。通过该命令可以轻轻松松地获取Java应用的运行状态和热点分布,帮助你快速定位问题并制定优化方案。执行以下命令开始 CPU 采样:

[arthas@1]$ profiler start
Profiling started

  上述命令表示启动Profiler,在默认的情况下生成CPU的火焰图;如果你需要分析内存分配情况,可以使用如下命令:

$ profiler start -e alloc

  等待采样完成。默认情况下,Arthas 会持续采样,直到你手动停止。可以设置倒计时只采集一段时间(例如 30 秒或 1 分钟),倒计时结束时自动停止采样。例如执行以下命令表示采样300秒并生成火焰图:

[arthas@1]$ profiler start -d 300
Profiling started
profiler will silent stop after 300 seconds.
profiler output file will be: /arthas-output/20250804-031051.html

  生成的火焰图默认是一个 .html 文件,保存到应用工作目录下的arthas-output文件夹,并在控制台输出文件保存路径。也可以使用format命令指定生成HTML格式的文件,只需要在stop后面输入-format html就可以了,语法糖如下:

[arthas@1]$ profiler stop -format html
OK
profiler output file: /arthas-output/20250804-031053.html

  这时会生成一个html的文件 /arthas-output/20250804-031053.html,把火焰图从服务器下载到本地后可以通过浏览器直接打开后,能看到这样的结构:

image

查看火焰图

  火焰图(Flame Graph)是一种用于透视函数调用栈的工具,它将函数调用关系以图形化的方式展示出来,帮助开发者更直观地理解程序的执行流程。在火焰图中,每一个矩形条代表一个函数调用的栈帧,矩形的宽度表示该函数调用的时间占比,颜色则可以根据需要进行自定义。

  下面介绍如何解读火焰图X轴与Y轴的含义。

  X 轴:表示函数采样次数的分布,也可以理解为CPU时间的分布比例。某个函数在采样中出现次数越多宽度越宽,说明该函数被采集的次数越多,占用的CPU时间越长。各个函数按字母顺序排列,没有时间上的先后关系。

  Y 轴:表示调用栈层级,每一层代表一个函数。调用栈越深,火焰就越高,顶部显示的是当前正在执行的函数,而下方则是它的父函数。

  颜色:不同颜色表示不同的函数,颜色没有特殊含义,主要是为了区分。因为火焰图表示的是cpu的繁忙程度,所以一般选择暖色调。

  通过火焰图可以快速定位性能瓶颈。主要看火焰图顶层的函数,占据宽度最大的函数就可能存在性能瓶颈,需要对它进行一个深度的性能分析,通过逻辑优化尽最大可能削去平顶。

🛠️常用选项

profiler:生成热点火焰图。
profiler start -d 30: 开始对应用中当前执行的活动采样 30 秒,采样结束后默认会生成 HTML 文件

分析内存分配:如果你想分析内存分配,可以使用profiler start -e alloc
profiler -h:查看帮助选项。
profiler list:展示支持的全部事件。
profiler status:打印采样功能已经运行了多少秒,只能在采样期间执行。
profiler getSamples:查询已经获取的样本数量,只能在采样期间执行。
jad:反编译指定已加载类的源码。
trace:查看方法调用链路上每个节点的耗时。
watch:查看方法请求参数和返回值等。
memory:查看 JVM 的内存信息。
sysenv:查看 JVM 的环境变量。
quit或exit:退出当前监控的进程,不会影响其他进程监控。
stop:停止所有监控和跟踪并退出Arthas。
dashboard:查看当前进程的实时数据面板。
thread:查看当前线程信息,包括堆栈信息。
sysprop:查看和修改JVM的系统属性。
sysenv:查看JVM的环境变量。
cls:清空当前屏幕区域的内容。

示例

  下面梳理出一个完整的、生成火焰图的示例:

// 启动 Arthas
$java -jar arthas-boot.jar

// 选择目标 Java 进程(例如输入 1)并单击回车键
1

[arthas@1]$ profiler status// 查询当前的采样状态
Profiler is not active
[arthas@1]$ profiler start// 开始 CPU 采样
Profiling started
[arthas@1]$ profiler status
Profiling is running for 5 seconds
[arthas@1]$ profiler getSamples
82
[arthas@1]$ profiler stop // 等待一段时间后停止采样并生成火焰图
OK
profiler output file: /arthas-output/20250801-031533.html
[arthas@1]$ profiler list
Basic events:
  cpu
  alloc
  nativemem
  lock
  wall
  itimer
  ctimer
Java method calls:
  ClassName.methodName

  希望这个案例能帮助你顺利生成火焰图并分析和解决应用性能问题!使用过程中,需要注意以下事项:

  • 性能影响:火焰图是通过采样生成的,频繁使用可能会影响应用性能,建议在测试环境中使用。
  • 文件下载:生成的火焰图需要从服务器下载到本地才能查看。
  • 权限问题:确保你有权限访问目标 Java 进程和生成文件的目录。你如果没有权限,就去求助运维。

CPU使用率高

  CPU 是程序运行的核心计算资源,一旦出现 CPU 使用率过高的现象,必定对大部分用户的访问耗时产生影响。针对这类问题亟需快速定位出有问题的线程,并获取该线程当前执行的代码位置。虽然使用 top + jstack 命令可以定位这类问题,但是Arthas 提供了更便捷的一体化工具dashboard,用于查看当前系统的实时数据面板。

# 调用线程看板,并刷新数据三次
[arthas@1]$ dashboard -n 3

  执行结果如下:

image

  dashboard 刷新三次后,就要找出 CPU 占用最高的用户线程的 ID进行把脉。示例中没有异常线程,我们用命令thread 31 看看线程31执行情况:

image

  当线程调用 AbstractQueuedSynchronizer 的方法awaitNanos()或await()时,会进入TIMED_WAITING状态。此时线程会暂停执行,直到超时或有条件的被触发。这种机制常用于定时任务、资源竞争等场景。

jad字节码文件反编译成源代码

  验证本地修改的代码是否推送到线上环境。有些时候你会发现测试环境一切正常,但生产环境就报错了。这类问题主要靠做好上线流程的管控,问题根源可能是打包的依赖库出现冲突,造成程序行为不一致;也可能是没有把最新代码发到线上。接下来,我们看看怎么用 Arthas 命令jad反编译字节码文件为Java源代码并查看是否存在发错版本问题。

  在Arthas中,jad命令提供了多种参数以满足不同场景的诉求。我们以Java类java.lang.String为例,演示如何使用jad进行反编译。

# 快速反编译整个类文件
jad java.lang.String

# 显示行号,调试利器
jad --lineNumber true java.lang.String

  jad可以用于精准反编译指定函数:如 jad java.lang.String hashCode 仅反编译java.lang.String类的hashCode方法:

image

  如果您想要反编译特定类的某个方法并保存反编译结果到指定目录,您可以这样操作:

jad --source-only packageName.MyClass myMethod -d /path/to/save/directory/myMethod.java

  这将只显示在package为packageName的类MyClass中方法名为myMethod的源代码,并且不会包含行号信息,同时将反编译过程中产生的临时class文件保存到/path/to/save/directory。

stack 输出方法被调用的调用路径链

  命令stack用于输出指定方法被调用的调用路径。开发过程中,经常遇到一个核心方法被多个方法调用,导致执行的路径非常多,不知道这个方法是从哪里被执行;如果想知道被谁调用了,就可以使用stack。语法糖如下:

stack packageName.MyClass myMethod

trace

  trace:追踪方法内部调用,即输出指定方法内每个调用节点上的耗时, 每次只能跟踪一级方法的调用链路,不能追踪子函数的耗时情况。语法糖:

// 统计myMethod的调用路径耗时
trace packageName.MyClass myMethod
// 根据调用耗时,找出大于200ms的调用路径
trace packageName.MyClass myMethod '#cost>200'

  如果一个函数耗时非常高,会高亮展示。下图中一个函数的执行耗时占整体的99.85%,所以,高亮展示了:
89d3f2277fd6d79d6a7c0c80bd7dd523.png

  例如用trace查看traceTest的耗时情况时,无法追踪到doSth101()的耗时。

    package 
    public void traceTest() {
           doSth1();
           doSth2();
           doSth3();
           doSth5();
    }
    
    public void doSth1() {
           doSth100();
           doSth101();
    }

性能分析

  遇到性能问题时,不要囿于思维局限,请跳出自家的“一亩三分地”,按照以下步骤排查:

# 1. 概览全局,寻找蛛丝马迹
dashboard -i 5000  

# 2. 生成火焰图定位CPU热点
profiler start
profiler stop

# 3. 追踪慢方法
trace *StringUtils substring '#cost>100'

# 4. 免重启热更新(大神展示技术深度的舞台,小白不要碰,别搞砸了线上环境)
redefine 把新生成的字节码文件在内存中执行

  破解线程阻塞之谜。如果遇到线程阻塞问题,可以使用thread排查:

# 1. 查看线程状态分布  
thread -b # 显示阻塞线程  

# 2. 监控锁竞争情况  
watch java.util.concurrent.locks.ReentrantLock getQueueLength

总结

  本文介绍了一些arthas常用命令的参数和具体使用方法,帮助大家进一步掌握arthas的使用,成为java在线诊断高手。Arthas能力矩阵:

问题类型 核心命令 备注
宏观把脉 dashboard 概览
定位CPU热点 profiler start/stop 生成火焰图
方法级追踪 trace/watch 精确到毫秒的性能分析
线程诊断 thread/thread -b 秒级定位线程阻塞源头
内存分析 heapdump/vmtool 不触发GC的内存快照
动态修复 jad/redefine 免重启热更新

  感谢大家看到最后,如文章有不足,欢迎大家在评论区支持,给予意见。如果觉得我的文章对你有帮助,那就给我点个赞吧。

Reference

posted @ 2025-08-19 09:37  楼兰胡杨  阅读(29)  评论(0)    收藏  举报