Arthas Profiler 火焰图实战:CPU 热点在哪一目了然

简介: 线上 CPU 飙高却不知道瓶颈在哪?一张 Arthas Profiler 火焰图采样定位,横宽即热点,一眼锁定最该优化的函数

大家好,我是程序员天天困。

这是「Arthas 线上诊断实战」系列第 5 篇,专门讲 Arthas Profiler 火焰图——怎么在 JVM 里采样出一张火焰图,以及火焰图到底该怎么读。前四篇 watch、trace、vmtool、mc+retransform 解决的是「功能 bug」,从这篇开始,我们碰「性能问题」。点个收藏,直接上手。

一、CPU 飙了,trace 一层一层追不够用了

当 CPU 水位飙到 90% 以上,最怕的不是没有工具,而是工具看得太窄。

前几篇里 watch 和 trace 负责定位问题——watch 看入参返回值,trace 看方法调用链的耗时分布。但碰到 CPU 飙高这种场景,trace 有个天生短板:一次只能盯一条方法链。你 trace 一个接口入口,它告诉你这个方法调了哪些子方法、各花了多久。可问题是——线上 CPU 飙高的时候,消耗往往不是集中在一个方法里,而是分散在几十条调用路径上。

举个例子:某个报表服务 CPU 突然飙到 95%,告警群里连着 @。拿 trace 追接口入口,结果入口方法本身耗时正常,真正的 CPU 消耗被拆散在十几个子方法里。trace 一条条追,追了五六条还没抓到重点——那种感觉就是拿手电筒在黑屋子里照路,照到哪是哪,看不到全貌。

这时候就得换思路了:不追单条链,直接用 profiler 对整个 JVM 采样,生成一张火焰图。图一出来,最宽的那条「火苗」直接指向一个藏在循环里的 Pattern.compile——这才是真正的元凶。

trace 的定位是「精确到某条链路」;profiler 的定位是「高空俯瞰、看全局哪里最热」。两者不矛盾,是配合关系。

二、火焰图是什么:把调用栈剖开给你看

火焰图最大的价值,是给你一个全局视野——所有线程的调用栈在同一张图上摊开,谁占的面积大,谁就是热点。

火焰图(Flame Graph):由 Linux 性能优化专家 Brendan Gregg 发明的可视化方法,把程序运行期间的调用栈采样结果画成一张「火焰」形状的图。你可以把它理解为「给程序拍了一张全身 X 光片,哪个函数的骨头最粗一眼就能看到」。

async-profiler:一个低开销的 Java 采样分析工具,Arthas 的 profiler 命令底层就是基于它实现的。你不需要额外安装 async-profiler,Arthas 启动时就自带了。

采样(Sampling):性能分析的一种方式——不记录每一次方法调用,而是每隔固定时间间隔(默认 10ms)「拍一张调用栈快照」。你可以理解为「路口架了个摄像头,每隔几秒拍一张」,拍得多了自然就能统计出哪条路最堵。

读火焰图,记住三个要素就行:

横轴不是时间,是采样占比。 火焰图把采集到的所有调用栈按字母顺序横向排好,相同路径的栈合并在一起。一个格子从左到右的宽度,代表它在采样中出现的次数占比——越宽,说明这个函数(或它的子调用)占用的资源越多,越可能是瓶颈。注意,左右排列顺序只是为了聚合,不代表执行先后。

纵轴是调用栈深度。 下面是调用者(父函数),上面是被调用者(子函数)。栈越深,图越高,像火焰往上窜。

颜色没有特殊含义。 暖色调只是方便区分不同函数,红色不代表「有问题」,绿色也不代表「没问题」——你只需要看宽度。

三、Arthas profiler 三步出图:start → status → stop

Arthas 的 profiler 命令核心就三步:开始采样、看采样状态、停止并生成火焰图。

前提还是先把 Arthas Attach 到目标 JVM(前面几篇讲过,java -jar arthas-boot.jar 选进程进到 [arthas@PID]$)。进来之后:

第 1 步,启动采样: 默认采 cpu 事件,每 10ms 采一次。

profiler start

终端回显 Profiling started 就说明采样已经跑起来了。

第 2 步,看看采了多少了: status 看采样状态,getSamples 看已采集的样本数。

profiler status
profiler getSamples

status 会告诉你当前在采哪种事件、跑了多久;getSamples 直接给你一个数字——样本数太少(比如个位数)说明采的时间不够,结果没代表性。

第 3 步,停止采样,生成火焰图:

profiler stop --format flamegraph

成功后会打印输出文件路径,比如 /tmp/arthas-output/20260728-092813.html

怎么打开看?Arthas 默认用 3658 端口提供 HTTP 访问,浏览器打开 http://localhost:3658/arthas-output/ 就能看到所有生成的火焰图文件,点击直接在浏览器里交互查看。Chrome 用户可能需要刷新一两次才能加载出来。

四、火焰图怎么读:先找最宽的那条火苗

读火焰图的口诀就一句:先找最宽的那条,然后顺着它往下看调用链。

拿到一张火焰图,别一上来就逐行读,那样会晕。正确的姿势是:

  1. 扫全局,找最宽的格子——它占的采样最多,就是你最该关注的热点。
  2. 往上看子函数——一个格子的宽度通常被它的子调用继承,真正在干活的往往是栈顶(最上面)那个。
  3. 往下看父函数链——理解这个热点是在什么业务场景下被触发的,才能判断该不该优化、怎么优化。

举个场景。假设 com.ttk 包里有个报表过滤服务,某天 CPU 直接打满:

// 示例:报表过滤服务,数据量大时 CPU 飙高
public class ReportFilterService {
   
    public List<ReportItem> filter(List<ReportItem> items, String keyword) {
   
        List<ReportItem> result = new ArrayList<>();
        for (ReportItem item : items) {
   
            // 问题:Pattern.compile 放在循环里,每次迭代都重新编译正则
            if (Pattern.compile(keyword).matcher(item.getContent()).find()) {
   
                result.add(item);
            }
        }
        return result;
    }
}

profiler 采样 60 秒后生成火焰图,最宽的一条赫然是 Pattern.compile——占了将近 40% 的宽度。往下看调用链:ReportFilterService.filter() → Pattern.compile()。原来正则编译被放在了循环里,数据量一大,光编译正则就把 CPU 吃干了。

修复也简单——把 Pattern.compile 提到循环外面只编译一次:

// 修复后:正则只编译一次
public List<ReportItem> filter(List<ReportItem> items, String keyword) {
   
    Pattern pattern = Pattern.compile(keyword);
    List<ReportItem> result = new ArrayList<>();
    for (ReportItem item : items) {
   
        if (pattern.matcher(item.getContent()).find()) {
   
            result.add(item);
        }
    }
    return result;
}

这就是火焰图的价值:它不需要你猜,图上最宽的那条直接告诉你答案。

可能有人会问:火焰图里有一大片 unknown / [unknown] 是什么意思?

unknown 通常出现在 native 帧或 JIT 编译的代码上——采样时拿不到对应的符号信息。如果 unknown 占比很小(比如 < 5%),直接忽略。如果占了一大块,可能是 async-profiler 的 C 栈收集方式没配对,试试加 --cstack fp(用 Frame Pointer 方式收集 native 栈)换一种模式重新采样。

五、不止 CPU:alloc / lock / wall 切事件

profiler 不只能看 CPU,换一个 --event 参数就能查内存分配、锁竞争、阻塞时间。

先用 profiler list 看看当前平台支持哪些事件:

profiler list

下面是我在 macOS 上跑出来的结果(Linux 上会多出 ctimer 和一批 perf events,比如 cache-missesbranches 等):

Basic events:
  cpu
  alloc
  nativemem
  lock
  nativelock
  wall
  itimer

日常最常用的是 cpu、alloc、lock、wall 这几个,itimer 作为补充:

event 看什么 典型场景
cpu CPU 占用采样 CPU 飙高,找最耗 CPU 的函数
alloc Java 内存分配量 频繁 GC、堆涨太快、找谁在疯狂 new 对象
lock Java 锁竞争耗时 线程阻塞、响应变慢、锁粒度太大
wall 墙钟时间(含等待) 请求整体慢但不一定是 CPU 问题
itimer CPU(备选方案) 容器里 perf_event 不可用时替代 cpu

nativememnativelock 分别看 native 层的内存分配和锁竞争,排查 JNI、第三方 native 库时才用得上,日常 Java 应用分析很少碰到。

切 event 的操作完全一样,只是 start 时多加一个参数。查内存分配热点就 --event alloc,查锁竞争就 --event lock

profiler start --event alloc
profiler start --event lock

我的习惯是:CPU 飙高先上 cpu,GC 频繁就切 alloc,接口卡但 CPU 不高就试 walllock

六、profiler 踩坑与进阶技巧

profiler 命令本身不难,真正决定结果质量的是采样时长和结果解读的判断力。

几个我实战中反复用到的技巧:

1)采样时长要够。 采 5 秒和采 60 秒,结果的代表性完全不一样。太短了可能恰好采到一段不典型的负载。我的经验是至少采 30 秒以上,业务有周期性波动的就覆盖一个完整周期。不想手动掐表的话,用 --duration 让它到点自动停。下面这条命令会在 120 秒后自动停止并生成火焰图:

profiler start --duration 120

2)--format md 直接出 Markdown 报告。 这是 Arthas 较新的功能,不用打开浏览器看 HTML,直接在终端拿到一份结构化的 Markdown 报告——包含 Top N 热点函数和调用树,方便贴到群里和同事讨论,或者丢给 AI 帮你分析:

profiler stop --format md

3)-t 分线程采样。 默认火焰图是把所有线程的栈混在一起的。如果你怀疑是某个特定线程在吃 CPU,加 -t 就能按线程分别出图,每条栈以线程名结尾:

profiler start -t

4)--include / --exclude 过滤噪音。 应用复杂时火焰图里会有一大堆框架代码(Spring、Jackson 之类),你可能只关心自己的业务代码。用 --include 只留关心的包路径,比如只看 com/ttk 包下的调用栈、排除 Unsafe.park(线程池 parked 状态的噪音):

profiler stop --include 'com/ttk/*' --exclude '*Unsafe.park*'

5)性能开销很低,但别长时间挂着。 async-profiler 是采样式的,不是字节码插桩,开销大约 1%~5%,生产环境完全可以接受。但我的建议是:采完就 stop,别让它一直跑着——长时间采样不仅结果文件变大,还占着 Arthas 的 agent 资源。

七、profiler vs trace:什么时候用哪个

profiler 和 trace 不是二选一,而是「先俯瞰定位区域,再 zoom in 看细节」的配合关系。

维度 profiler trace
视野 全局,所有线程所有方法的采样 单个方法 / 类的方法调用链
看什么 CPU / 内存 / 锁的采样占比 单次调用的逐层耗时
适合 CPU 飙高、找全局热点在哪 已知入口,追某条链路哪层最慢
原理 定时采样,统计频率 每次方法进出都记录
开销 极低(采样式) 高频方法有一定开销

我的推荐流程是:先用 profiler 俯瞰全局,定位到热点区域 → 再用 trace 精确追那一条链路的耗时分布 → 最后用 watch 或 vmtool 确认入参和对象状态。 三步走,从粗到细,不遗漏也不浪费时间。

结语

Arthas Profiler 火焰图给你的是一张「全局热力图」——哪里最宽,哪里就最值得优化。trace 帮你看清单条链路的细节,watch 帮你确认方法入参返回值,mc+retransform 帮你热修方法体,而 profiler 帮你在性能问题面前先看到全局。五篇下来,你的 Arthas 工具箱已经能覆盖「定位 → 分析 → 修复 → 验收 → 性能分析」的完整闭环了。

本文命令参数以 Arthas Profiler 官方文档为准,版本更新了以官网为准。


我是程序员天天困,持续分享编程干货。觉得有用的话记得点赞收藏和关注~也欢迎在评论区聊聊:你有没有用火焰图揪出过意想不到的 CPU 热点?当时最宽的那条是什么函数?

相关文章
|
6天前
|
人工智能 JSON 安全
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
阿里云AI安全产品联动防御Fastjson攻击
2032 9
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
|
6天前
|
云安全 人工智能 安全
|
7天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max-Preview深度全解析:2.4万亿参数旗舰MoE模型+Token Plan限时优惠完整落地指南
2026年7月,全新旗舰级混合专家大模型Qwen3.8-Max-Preview正式开放抢先体验,作为通义千问Qwen3系列规格最高、综合推理能力顶尖的新一代模型,该模型总参数量达到2.4万亿(2.4T),是当前线上可调用的原生多模态旗舰模型,综合推理水准对标海外顶级Fable 5模型,在复杂工程开发、长文档深度分析、多步骤智能体自治、跨境多语言创作、海量数据挖掘五大高难度业务场景实现跨越式性能提升。
885 1
|
7天前
|
人工智能 自然语言处理 数据挖掘
最新版通义千问(Qwen3.8-Max-Preview)功能介绍
2026年,通义千问正式推出全新旗舰级大模型 **Qwen3.8-Max-Preview 预览版**,作为首款突破万亿参数规格的新一代基座模型,该模型总参数量达到**2.4万亿**,采用全新迭代的MoE混合专家架构,综合推理性能、长文本处理、多模态理解、复杂任务规划能力全面超越前代Qwen3.7-Max版本,整体实力跻身全球第一梯队,可对标海外顶级旗舰模型,是当前面向复杂工程开发、多智能体协同、超长文档解析、专业办公自动化场景的最优国产基座模型。
887 0
|
8天前
|
人工智能
Qwen3.8抢先体验!正式版即将发布并开源!
千问Qwen3.8即将开源,参数达2.4T,进化速度以“天”计,实力媲美Fable 5。预览版Qwen3.8-Max已上线阿里Token Plan等平台,限时优惠:日间Credits低至1折,夜间更优,个人/团队版月付仅35元起!
895 38
|
5天前
|
自然语言处理 测试技术 API
通义千问Qwen3.8-Max-Preview全功能解析:2.4万亿参数旗舰模型深度使用指南
在大模型技术持续迭代的当下,通义千问推出的Qwen3.8-Max-Preview作为新一代旗舰预览版模型,凭借2.4万亿参数的超大规模、多模态融合能力与全场景适配特性,成为开发者与企业用户探索AI应用的核心工具。该模型采用稀疏混合专家(MoE)架构,是通义千问首个突破万亿参数的多模态模型,可同时处理文本、图像、视频与文档等多种数据形态,在全栈代码开发、复杂逻辑推理、长文档分析与多智能体协作等场景实现跨越式升级。本文将全面拆解Qwen3.8-Max-Preview的核心功能,详解API调用流程与配置方法,覆盖多场景实战技巧,帮助用户快速掌握这款旗舰模型的使用方法,充分释放其性能潜力。
430 1
|
8天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南
Qwen3.8-Max-Preview是通义千问Qwen3系列旗舰MoE大模型,参数达2.4万亿,综合推理能力居行业第一梯队。支持思考/快速双模式,擅长大模型五大高难场景。现于阿里云百炼Token Plan、Qoder及QoderWork上线体验,个人版低至39元/月。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
658 1
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南