android - 性能 - Perfetto - cpu 分析教程及例子

Android Perfetto CPU 性能完整分析教程(含界面截图标注、抓取配置、轨道详解、4大实战案例、SQL量化统计)

一、核心前置认知:Perfetto CPU 分析优势与排查目标

1.1 对比 Systrace / Android Studio Profiler

工具 CPU分析能力 短板 适用场景
Perfetto 1. 内核级CPU调度、大小核、频率、休眠C-State全量采集;2. Java/Native双栈采样火焰图;3. 线程状态Running/Sleep/D/R完整标记;4. Perfetto SQL量化CPU耗时、利用率、核心负载;5. 与丢帧、GC、Binder、锁等待联动分析 Java采样为抽样模式,极小函数偶有漏采 高CPU耗电、后台持续唤醒、主线程CPU阻塞、死循环、大小核调度不合理、锁竞争自旋耗CPU
AS Profiler 一键可视化,上手简单 看不到内核调度、无法区分大小核、无线程阻塞根因 粗略查看进程CPU百分比
Systrace 基础调度采集 无SQL统计、Native栈弱、UI交互老旧 老旧设备快速排查

1.2 CPU问题4大类根因(直接对号入座)

  1. 纯计算CPU占用过高:死循环、大量字符串处理、正则、JSON频繁解析、图片逐像素处理;
  2. 线程频繁唤醒(后台耗电大户):Handler无限轮询、定时器未销毁、广播频繁触发、线程自旋等待;
  3. CPU调度异常:主线程/关键工作线程被调度到小核、CPU频繁降频、被其他进程抢占CPU时间片;
  4. 阻塞假象高耗时:锁竞争(futex)、磁盘IO阻塞、Binder同步等待(线程Sleep/D状态,Wall时间长但真实CPU运行时间短)。

1.3 环境前置

  • 无需Root即可抓取完整CPU调度数据;仅Native调用栈采样需要Root
  • 分析网页:https://ui.perfetto.dev/
  • 手机开启:USB调试、开发者选项打开。

二、Trace抓取两种方式(附录制页截图点位说明)

方式一:网页可视化录制(新手首选,截图区域一一对应)

步骤1:进入录制面板

打开 ui.perfetto.dev → 左上角 Record new trace

页面三大分区(截图可直接对照查找):

  1. 左侧 Data Sources 数据源勾选区(CPU必勾清单)
【内核调度核心(必选)】

✅ Scheduling → CPU Scheduling(线程在各CPU核心调度切片,最重要轨道)

✅ Scheduling → CPU Frequency(CPU大/小核动态调频,判断是否降频锁核)

✅ Scheduling → CPU Idle(C-State深度休眠,判断后台是否耗电)

✅ Scheduling → Thread states(线程运行状态R/S/D/W标记)

【应用层定位耗时代码(必选)】

✅ App → Java Method Sampling(Java方法采样,右键生成CPU火焰图)

✅ App → Atrace apps(view、gfx、activity,联动UI线程CPU消耗)

【辅助关联排查(建议勾选)】

✅ IPC → Binder transactions(Binder阻塞导致线程Sleep)

✅ Memory → Android ART GC(频繁GC占用CPU)

✅ Graphics → Frame timeline(高CPU引发丢帧联动验证)

步骤2:参数配置(截图输入框位置)

  1. Package name filter:填写App包名,过滤只抓取目标进程
  2. Recording duration:60~120s,复现高CPU场景
  3. Buffer size:64MB,防止长Trace截断丢失调度数据

步骤3:启动录制&复现场景

  1. Add ADB device 选择已连接设备 → Start Recording
  2. 手机端复现:页面滑动、后台挂起、列表快速刷新、持续网络解析、定时器轮询
  3. 结束自动下载 .perfetto-trace 文件,拖拽网页打开分析

方式二:adb命令一键抓取(自动化脚本、批量回归使用)

完整版CPU专项抓包命令(直接复制执行)

bash 复制代码
adb shell perfetto -o /data/misc/perfetto-traces/cpu_analysis.trace -t 90s -a --txt <<EOF
buffers { size_kb: 65536 fill_policy: DISCARD }
# CPU内核调度、频率、休眠、线程状态
data_sources { name: "linux.sched" }
data_sources { name: "linux.cpu_freq" }
data_sources { name: "linux.cpu_idle" }
data_sources { name: "linux.thread_state" }
# Java方法采样定位热点函数
data_sources { name: "android.java_hprof" java_hprof_config { sampling_interval_ms: 10 } }
# ATrace UI、GC、Binder联动
data_sources { name: "android.atrace" atrace_categories: "view gfx activity" }
data_sources { name: "android.gc" }
data_sources { name: "android.binder" }
EOF

# 拉取文件到本地
adb pull /data/misc/perfetto-traces/cpu_analysis.trace ./

三、Perfetto UI CPU轨道逐层深度解读(全截图点位标注)

整体排查标准流程:

宏观看CPU核心负载&频率 → 定位耗CPU最高线程 → 查看线程运行状态(判断计算/阻塞)→ 火焰图锁定热点方法 → SQL量化统计

3.1 顶层内核CPU全局轨道(截图最上方区域)

轨道1:CPU Scheduling(CPU X 每一行代表一个物理核心)

  1. 每一行=一个CPU核心(CPU0~CPU7,区分小核/大核);
  2. 彩色色块=某线程在该时间片占用此CPU运行,色块越长、密度越高=该核心负载越高;
  3. 快速判定:
    • 单个核心色块填满100%:单核跑满死循环/密集计算
    • 小核长期满载、大核空闲:线程被调度到低效小核,性能差、易卡顿
    • 所有核心零散碎片化色块:线程频繁频繁切换调度,上下文切换损耗CPU。

轨道2:CPU Frequency 频率曲线

  • 纵坐标=主频(MHz/GHz),数值越高性能越强;
  • 触发瞬间飙升:代码密集计算拉高频率;
  • 持续低频锁频:手机温控降频、后台省电策略,导致代码执行变慢、耗时拉长;
  • 优化点:高频计算任务确保调度到大核运行。

轨道3:CPU Idle(C-State休眠)

  • 深度Cx状态=CPU进入低功耗休眠;
  • 后台App大量线程频繁唤醒、退出Idle → 持续耗电、发热,是后台高CPU耗电核心标志。

3.2 进程&线程轨道(核心分析层,截图高频操作点)

第一步:筛选目标App进程

顶部搜索框输入包名,只保留目标进程,展开所有子线程:

main(主线程)、RenderThread、pool-xxx工作线程、自定义WorkThread、Binder线程

第二步:Thread State 线程状态字母标记(截图小标签)

判断CPU高耗本质的黄金规则

状态标记 全称 含义 问题类型 优化方向
R Running CPU正在真实执行代码 纯计算耗CPU 火焰图优化热点算法
R(Runnable) 就绪队列等待 CPU被其他进程抢占 调度竞争 优化任务执行时机,避开系统负载高峰
S Sleeping 主动休眠(wait/lock/Handler等待) 阻塞等待,不耗CPU 无需优化,正常等待
D Uninterruptible Sleep 不可中断休眠(磁盘IO、文件读写) 主线程IO阻塞卡顿 全部移至子线程异步执行
关键判定口诀(实战必记)
  1. Wall耗时 ≈ CPU Running总时长 → 纯代码计算导致高CPU,直接打开火焰图找函数;
  2. Wall耗时 >> CPU Running时长 → 线程大量Sleep/D等待,问题在锁、IO、Binder、系统调用,不要优化Java代码。

第三步:CPU火焰图定位热点代码(截图右键操作)

操作步骤(截图点位)
  1. 在耗时长的线程切片(如main线程doFrame、WorkThread长Running块)右键 → Open flamegraph
  2. 视图切换为 Time(CPU执行耗时)
  3. 纵向=调用栈,横向柱子宽度=占用CPU时间占比,最宽叶子方法即为CPU热点元凶;
  4. 右键Focus聚焦调用栈,复制方法名全局检索代码。
两种典型火焰图形态
  1. Java层热点onBindViewHolder、字符串处理、正则解析、Json序列化;
  2. Native层热点:libxxx.so内部解码、加密、循环计算(需要Root采样栈)。

3.3 高阶:Perfetto SQL 量化CPU数据(底部Query面板直接执行)

SQL1:统计App所有线程CPU总耗时(TOP20耗CPU线程,最常用)

sql 复制代码
SELECT
  thread.name AS 线程名,
  SUM(sched.dur)/1e9 AS CPU运行总秒数
FROM sched
JOIN thread USING (utid)
JOIN process USING (upid)
WHERE process.name = 'com.xxx.你的应用包名'
GROUP BY thread.utid
ORDER BY CPU运行总秒数 DESC
LIMIT 20;

SQL2:单线程在各CPU核心运行分布(排查是否被调度到小核)

sql 复制代码
SELECT
  cpu AS CPU核心编号,
  SUM(sched.dur)/1e6 AS 运行毫秒数
FROM sched
WHERE utid = (SELECT utid FROM thread WHERE process_name='com.xxx.app' AND name='main')
GROUP BY cpu;

SQL3:线程状态耗时占比(区分计算/阻塞)

sql 复制代码
SELECT
  CASE thread_state
    WHEN 'Running' THEN 'CPU运行(耗性能)'
    WHEN 'S' THEN '休眠等待'
    WHEN 'D' THEN 'IO阻塞'
    ELSE '其他状态'
  END AS 状态类型,
  SUM(dur)/1e6 AS 总耗时ms
FROM thread_state
JOIN thread USING (utid)
WHERE process_name = 'com.xxx.app'
GROUP BY 状态类型;

SQL4:计算进程整体CPU利用率

sql 复制代码
INCLUDE PERFETTO MODULE linux.cpu.utilization.process;
SELECT process_name, avg_utilization_pct FROM cpu_utilization_per_process
WHERE process_name = 'com.xxx.app';

四、4个真实CPU高占用实战案例(Trace截图特征+错误代码+修复)

案例1:子线程死循环无限轮询,后台CPU持续30%+耗电(最高频)

现象

App退到后台,手机发热、耗电飞快,top命令查看进程CPU居高不下。

Perfetto Trace截图特征

  1. CPU调度轨道:某自定义PollThread线程在小核持续占满Running状态,色块不间断;
  2. CPU Frequency持续高频不降,Idle休眠几乎为0;
  3. SQL查询该线程CPU运行时间断层式第一。

错误代码

java 复制代码
// 退出页面未终止死循环,后台无限空跑
new Thread(() -> {
    while (true) {
        checkData();
        // 无sleep,100%占用单核CPU
    }
}).start();

修复方案

  1. Activity/Fragment销毁时标记isRunning=false终止循环;
  2. 循环内增加Thread.sleep(500)降低轮询频率;
  3. 使用Handler.removeCallbacksWorkManager替代裸线程死循环。

案例2:RecyclerView onBindViewHolder 密集计算导致滑动CPU飙升+丢帧

现象

列表上下滑动明显卡顿,帧率下降,CPU瞬间拉满。

Trace截图特征

  1. Main主线程大量Running状态,Choreographer#doFrame耗时超标;
  2. 火焰图最宽模块:onBindViewHolder内部Html解析、正则匹配、大图同步解码;
  3. FrameTimeline伴随红色App Jank丢帧。

错误代码

java 复制代码
@Override
public void onBindViewHolder(VH holder, int pos) {
    // 主线程实时富文本解析,CPU密集运算
    CharSequence text = Html.fromHtml(item.content);
    // 循环字符串正则替换
    text.replaceAll("@\\w+", "");
}

修复

  1. 富文本、正则、Json解析全部子线程预计算缓存;
  2. Bind只做UI赋值,杜绝耗时逻辑;
  3. 使用DiffUtil局部刷新,减少onBind频繁调用。

案例3:锁竞争自旋等待(线程大量R就绪态,假高CPU)

现象

CPU使用率偏高,但应用无明显计算逻辑,偶发ANR。

Trace截图特征

  1. 线程状态大量 R(Runnable)就绪等待,Running实际时间很短;
  2. 内核切片出现大量futex锁等待系统调用;
  3. Wall总耗时远大于真实CPU运行时长。

根因

多线程并发synchronized锁粒度太细、频繁争夺同一把锁,内核自旋消耗CPU资源。

修复

  1. 缩小锁范围,只锁定临界代码块;
  2. 使用ReentrantLock公平锁、CAS原子类替代重量级同步锁;
  3. 减少并发线程数量。

案例4:主线程文件IO/D磁盘阻塞(D状态,卡顿+间接拉高CPU)

现象

点击页面跳转瞬间卡顿,CPU小幅冲高,页面打开缓慢。

Trace截图特征

Main线程长时间标记 D(不可中断休眠),对应sys_read/sys_write系统调用长条切片。

修复

  1. SharedPreferences使用apply()异步提交,禁用commit()同步写入;
  2. 本地文件读写、数据库查询全部放入IO线程池;
  3. 冷启动大量磁盘操作延迟至首帧渲染完成后执行。

五、全界面截图点位汇总(打开Perfetto直接对应查找)

  1. 录制配置页截图点:Record new trace → Scheduling全量勾选、Java Method Sampling勾选、包名过滤输入框;
  2. CPU总览截图点:CPU Scheduling多核色块轨道、CPU Frequency频率折线、CPU Idle休眠状态;
  3. 线程状态截图点:Thread State R/S/D字母小标签标记;
  4. 火焰图截图点:线程切片右键Open flamegraph、Time耗时视图、Focus聚焦调用栈;
  5. SQL截图点:底部Query输入框执行CPU线程耗时统计表;
  6. 联动辅助截图点:GC密集竖线、Binder长条事务块。

六、高频踩坑避坑清单

  1. Java Method Sampling是抽样采集:执行极短的微小函数不会被采样到,重度循环/长耗时方法100%捕获;Release包混淆后方法名为a.b.c,需上传mapping.txt符号表还原;
  2. 区分真高CPU(Running计算)假高CPU(R/D/S阻塞等待),二者优化方向完全不同;
  3. 不要只看整机CPU百分比,必须看单核心满载、大小核调度,小核跑满即使整机占用低依然卡顿;
  4. 后台CPU问题重点看CPU Idle休眠状态,频繁退出休眠=耗电发热根源;
  5. CPU高占用经常联动内存抖动:频繁GC STW暂停线程同时消耗CPU,需结合内存轨道一起分析。

七、团队标准化CPU排查SOP(可直接落地执行)

  1. 使用完整调度、频率、线程状态、Java采样配置抓取CPU Trace;
  2. 宏观查看多核负载与调频,判断是否单核打满/小核调度;
  3. SQL导出TOP耗CPU线程,定位异常工作线程;
  4. 查看线程State区分纯计算或阻塞等待;
  5. Running占比高→打开火焰图优化热点函数;阻塞占比高→排查锁/IO/Binder;
  6. 优化后重抓Trace验证:CPU满载色块消失、后台线程进入深度Idle休眠、火焰图热点方法耗时大幅下降。

需要我补充:

  1. 每一步标注版示意图文案(可直接AI生成高清配图)
  2. Windows一键抓取CPU Trace批处理脚本;
  3. Compose重组、图片解码专项CPU优化Perfetto排查方案?
相关推荐
AFinalStone3 小时前
Android 7系统休眠唤醒(七)内核层—wakelock与autosleep机制
android·休眠唤醒
starvapour3 小时前
在关机键损坏的情况下让手机关机
android·adb·手机
AFinalStone4 小时前
Android 7系统休眠唤醒(五)休眠全链路
android·电源管理·休眠唤醒
zzq77975 小时前
Android 风险识别机制实测:从录屏悬浮窗到远程控制的边界评估
android·安全·安卓·app加固·御盾安全
码云数智-园园5 小时前
MySQL 慢查询排查完整流程
android
码云骑士5 小时前
【Android Performance】进程关联启动治理详解——从唤醒链分析到自启动管控的完整方案
android
心念枕惊6 小时前
PHP 在领域驱动(DDD)设计中的核心实践
android·开发语言·php
蜡台6 小时前
Android WebView 设计指南
android·java·kotlin
晓梦林7 小时前
[Dest0g3 520迎新赛]EasyPHP-学习笔记
android·笔记·学习