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

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

综述

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

  1. 不知道哪儿慢:即不知道哪个函数占用大量 CPU 时间,CPU 使用率过高。
  2. 不知道谁是瓶颈:即不知道哪个方法被频繁调用导致服务出现性能问题,如存在锁竞争的热点函数大概率是系统瓶颈。
  3. 不知道为何输出结果不符合预期。
  4. 不知道系统应用为什么频繁出现FULL GC,而且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,故需要安装;否则,直接运行 Arthas 即可。返回版本号时,终端执行结果如下:

$ java -jar arthas-boot.jar --version
Picked up JAVA_TOOL_OPTIONS:  -javaagent:/home/admin/.opt/AliyunJavaAgent/aliyun-java-agent.jar -Doneagent.plugin.arms-agent.enabled=true -Darms.licenseKey=efwsi75gij@ff5c382fd39f5e7 -Darms.agent.env=ACSK8S -Darms.agent.podinfo.path=/etc/podinfo -Darms.appId=efwsi75gij@68ea36d60a72988 -Daliyun.javaagent.regionId=eu-central-1 -Dmse.enable=true -Dmse.licenseKey=efwsi75gij@c97ae90801008ca -Dmse.appId=efwsi75gij@68ea36d60a72988 -Dmse.appName=hisavana-prod-hisavana-traffic-dispatch -Dmse.namespace=default
[INFO] JAVA_HOME: /opt/java/openjdk/jre
[INFO] arthas-boot version: 4.1.8
[INFO] JAVA_TOOL_OPTIONS:  -javaagent:/home/admin/.opt/AliyunJavaAgent/aliyun-java-agent.jar -Doneagent.plugin.arms-agent.enabled=true -Darms.licenseKey=efwsi75gij@ff5c382fd39f5e7 -Darms.agent.env=ACSK8S -Darms.agent.podinfo.path=/etc/podinfo -Darms.appId=efwsi75gij@68ea36d60a72988 -Daliyun.javaagent.regionId=eu-central-1 -Dmse.enable=true -Dmse.licenseKey=efwsi75gij@c97ae90801008ca -Dmse.appId=efwsi75gij@68ea36d60a72988 -Dmse.appName=hisavana-prod-hisavana-traffic-dispatch -Dmse.namespace=default
Local versions:
Remote versions:
 4.1.8
 4.0.5
 ...

下载和安装 Arthas

  如果还没有安装 Arthas,请通过以下命令下载启动引导包,分分钟接入生产环境:

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

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

image

  【Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.】的意思是arthas已经完成自动扫描当前系统上正在运行的所有 Java 进程并识别到Java进程,请在选择进程号后单击回车键,选中你要生成火焰图进行分析的应用。输入进程号 1 并回车就是选择目标进程。关键日志信息如下:

pid          1                                                                  
start_time   2026-04-20 02:50:36.032 -->服务部署时间
current_time 2026-04-21 01:38:40.923 -->当前时间

[arthas@1]$ 

温馨提示,arthas依赖下载发生在首次执行 java -jar arthas-boot.jar 时。当你第一次运行它时,它会自动从远程仓库下载完整的 Arthas 依赖到本地,然后接入到你选择的 Java 进程上,从而可以在不重启、不修改代码的情况下对该进程进行诊断和调试,帮助我们完成性能瓶颈分析。

生成CPU火焰图

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

[arthas@1]$ profiler start
Profiling started

  上述命令表示启动Profiler,生成CPU的火焰图。启动后等待采样完成即可。默认情况下,Arthas 会持续采样,直到你使用命令 profiler stop 手动停止。也可以使用format命令指定生成HTML格式的文件,只需要在stop后面输入-format html就可以了,语法糖如下:

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

  这时会生成一个html的文件/arthas-output/20260421-031053.html。可以设置只采集一段时间(例如 30 秒或 1 分钟),倒计时结束时自动停止采样并生成CPU火焰图HTML文件。例如执行以下命令表示采样300秒并生成火焰图:

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

  生成的火焰图默认是一个 .html 文件,保存到应用工作目录下的arthas-output文件夹,并在控制台输出文件保存路径。把火焰图从服务器下载到本地并通过浏览器直接打开后,能看到这样的结构:

image

如果需要生成结构化的 Markdown 报告,便于直接复制到当今风靡全球的大语言模型(Large Language Model,缩写LLM),请使用如下命令:

profiler start -d 300 --format md 

查看CPU火焰图

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

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

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

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

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

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

生成内存分配火焰图

  内存分配火焰图(Memory Allocation Flame Graph)适合在哪些场景使用?它使用于排查系统频繁full GC、内存泄漏、高内存分配代码、对象频繁创建等场景。如果你需要分析Java应用程序在运行时的内存分配行为,可以使用如下命令:

# 第1步:启动内存信息采样,设置5分钟自动停止采样
[arthas@1]$ profiler start -e alloc --duration 300
Profiling started
profiler will silent stop after 300 seconds.
profiler output file will be: /arthas-output/20260420-013900.html
# 第2步:按照控制台提示静悄悄的等待5分钟,期间执行业务,查看其它监控指标

# 第3步:下载采样后生成HTML文件,然后分析系统内存瓶颈

说明:

  • --duration 300 会让采样在5分钟后自动停止
  • 在项目根目录文件夹arthas-output中自动生成HTML格式的内存火焰图,不必手动执行 profiler stop

  下面看看如何解读内存分配火焰图:

  • X 轴(宽度):代表内存分配量,越宽表示分配越多;

  • Y 轴(高度):代表调用栈深度,从下往上是调用顺序;

  重点关注:顶部宽大的“平顶”代表直接分配内存的地方,如果发现异常宽的分支,则可能存在过度分配或数据处理问题。

CPU 使用率飙高

  使用如下thread命令显示 CPU 使用率 Top 5 的线程堆栈,间隔3秒后进行第二次采样:

thread -n 5 -i 3000

  -i 3000 就是让 Arthas 观察 1 秒再告诉你各个线程 CPU 消耗情况,结果比默认采样周期更精准。从上述命令找到可能出问题线程id(如 782)后,执行如下命令查看完整堆栈信息,定位此线程是否存在问题:

thread 782

如果确认它是罪魁祸首,则顺藤摸瓜深挖问题代码,快速定位死循环、正则回溯或者频繁 GC 等问题。

接口响应慢请求卡顿

  如果用户反馈页面打不开、接口频繁超时,则使用如下命令排查:

  • trace 命令追踪方法调用路径及耗时(如 trace com.example.Service method)

  • watch 监控方法入参/返回值

  • stack 查看方法被谁调用(调用链)

快捷定位数据库慢查询、外部 API 超时、锁竞争等瓶颈。

内存泄漏

在服务内存持续增长,垃圾回收(GC)无法有效释放内存导致频繁 Full GC时,可以使用命令 dashboard 实时查看 JVM 内存、GC、线程状态。它是 Arthas 的实时监控面板命令,类似 Linux 的 top命令。

指标 含义
Memory JVM 内存使用情况快照(堆、非堆、Eden、Survivor、Old 区)
GC GC 次数和耗时(Young GC / Full GC)
Thread 线程总数、活跃线程、Daemon 线程等
Runtime JVM 启动时间、系统负载、CPU 核心数、类加载数等
Top Threads CPU 使用率最高的前几个线程(带堆栈摘要)

执行dashboard后,默认每5秒刷新一次,按键盘 Ctrl + C 可以停止刷新,返回 Arthas 命令行。

Full GC 频繁说明内存压力大或者垃圾回收器配置不合理。CPU 占用极高的线程可能死循环或热点方法,需要重点关照。如果Old 区 GC 后仍不回落,则说明存在强烈地内存泄漏。

命令 作用
dashboard -i 2000 每 2 秒刷新一次(单位:毫秒)
dashboard -n 10 刷新 10 次后自动退出
dashboard -i 3000 -n 5 每 3 秒刷新一次,共刷新 5 次后主动退出

🛠️常用选项

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/20260801-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%,所以,高亮展示了:

image

  例如用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:38  楼兰胡杨  阅读(1897)  评论(0)    收藏  举报