2. Android性能分析基础
在进入 Perfetto 的具体操作之前,必须先建立一套“性能语言”。否则抓到的 trace 只是一堆彩色条带,无法判断问题属于哪一类、严重到什么程度、应该由谁优化。本节围绕四个问题展开:看哪些指标、瓶颈通常出现在哪里、传统工具为什么不够用、Perfetto 在整套分析体系中处于什么位置。
一、核心性能指标:从用户感知到系统量化
Android 性能指标可以分为“用户可感知层”和“系统可测量层”。用户感知层描述卡不卡、烫不烫、掉不掉帧;系统可测量层则把这些感受拆成可采集的数值。二者必须建立映射,否则优化容易跑偏。
- 流畅度:帧率 FPS、帧耗时 Frame Time、掉帧数 Jank、卡顿率。60Hz 屏幕单帧预算约 16.67ms,120Hz 约 8.33ms,超过预算即可能掉帧。
- 响应速度:冷启动耗时、热启动耗时、界面切换时延、点击到首帧的延迟(TTFD)。
- CPU:各进程/线程的 CPU 占用率、运行时长、调度延迟、大小核分布、频率与降频情况。
- 内存:PSS/RSS、Java 堆、Native 堆、Graphics 显存、内存泄漏增长趋势、GC 次数与暂停时间。
- 功耗与温度:电流、电压、功耗曲线、CPU/GPU/屏幕耗电占比、温升与热限频。
- I/O 与网络:磁盘读写延迟、文件锁等待、网络请求耗时与重传。
二、常见瓶颈:问题通常藏在哪一层
Android 是分层系统,瓶颈可能出现在应用层、框架层、Native 层、内核层甚至硬件层。经验上,绝大多数卡顿与启动慢可以归入以下几类:
- 主线程阻塞:在主线程做磁盘 I/O、网络、大循环、复杂布局解析或同步锁等待,直接导致帧超时。
- 过度绘制与布局层级过深:GPU 填充压力大、measure/layout 次数多,表现为渲染耗时高。
- 频繁 GC 与内存抖动:短时间内大量对象分配触发 GC,造成主线程暂停。
- 锁竞争与线程调度:多线程争抢同一把锁,或线程被长时间挂起,表现为“CPU 不忙但就是慢”。
- Binder 跨进程调用:系统服务响应慢或调用频繁,形成跨进程等待链。
- CPU 降频与热限:长时间高负载后频率下降,性能突然变差。
- I/O 抖动:低内存回收、日志写入、数据库操作引发磁盘等待。
这些瓶颈往往相互叠加。例如内存抖动引发 GC,GC 暂停主线程,主线程阻塞又导致掉帧。因此分析工具必须能同时呈现 CPU、内存、渲染、Binder、I/O 等多条时间线,并支持跨进程关联。
三、传统工具的局限:为什么需要新的分析方式
在 Perfetto 普及之前,Android 开发者常用 Logcat、Systrace、Traceview、MAT、top、dumpsys 等工具。它们各有价值,但在复杂场景下存在明显短板:
| 工具 | 主要能力 | 典型局限 |
|---|---|---|
| Logcat | 查看应用与系统日志 | 无时间线、无跨进程关联,难以定位卡顿根因 |
| Systrace / atrace | 采集系统与框架关键路径 | 数据维度有限,内存/功耗/长时间采集能力弱,界面老旧 |
| Traceview / Method Tracing | Java 方法级耗时 | 开销大、易失真,不适合线上或长时间分析 |
| MAT / LeakCanary | 内存快照与泄漏检测 | 偏静态分析,难以与 CPU、渲染时间线对齐 |
| top / dumpsys | 瞬时状态与统计 | 采样粒度粗,缺少因果链与精确时间戳 |
四、Perfetto 的定位:统一采集、统一时间线、统一分析
Perfetto 是 Android 官方推荐的下一代性能分析平台,它并不是单一工具,而是一套从采集、存储到可视化的完整体系。其核心定位可以概括为三点:
- 统一数据源:通过 trace 协议同时采集 ftrace、atrace、进程统计、内存计数器、GPU、功耗、Binder 等多类数据。
- 统一时间线:所有事件带统一时钟,支持跨进程、跨线程、跨子系统对齐,形成完整因果链。
- 统一分析入口:既可用 Web 端 Perfetto UI 交互分析,也可用 trace_processor 与 SQL 做批量、自动化分析。
下面这张图展示了从问题现象到 Perfetto 分析定位的整体关系:左侧是用户可感知问题,中间是系统指标与瓶颈层,右侧是 Perfetto 采集到的多源时间线,最终汇聚到根因判断。
五、一个典型场景:滑动卡顿如何用 Perfetto 定位
假设用户反馈“商品列表滑动时偶尔卡一下”。传统做法可能先看 Logcat,发现没有明显异常;再用 Systrace 抓一段,看到主线程有长条,但不知道在等谁。改用 Perfetto 后,流程更清晰:
# 采集包含调度、Binder、内存、渲染的 trace
adb shell perfetto -o /data/misc/perfetto-traces/trace.pftrace -t 10s \
sched freq idle am wm gfx view binder_driver hal dalvik camera input res memory
# 拉取到本地
adb pull /data/misc/perfetto-traces/trace.pftrace .
# 用 trace_processor 查询主线程阻塞最长的切片
SELECT name, dur, ts
FROM slice
WHERE track_id IN (SELECT id FROM track WHERE name LIKE '%main%')
ORDER BY dur DESC
LIMIT 10;
在 Perfetto UI 中,可以同时看到:主线程某段 RecyclerView 布局耗时过长;同一时间段内 RenderThread 在等待;再往下看,发现是某个图片解码在主线程执行,并且触发了 Binder 调用等待系统服务。这样,根因就从“卡顿”收敛到“主线程图片解码 + Binder 等待”这一具体问题。
六、复盘与常见误区
- 只看平均值:平均帧率正常不代表没有卡顿,必须看 P95、P99 与最大帧耗时。
- 把等待当执行:时间线上的长条可能是等待,需结合唤醒链与依赖关系判断。
- 忽略采集开销:过重的采集配置会改变系统行为,应控制 buffer 与数据源范围。
- 缺少基线:没有优化前的 trace 对比,很难证明改动有效。
- 过早下结论:先建立完整时间线,再下判断,避免被单条日志或单个长条误导。
掌握这些基础后,后续学习 Perfetto 的采集配置、UI 操作与 SQL 查询时,就能始终围绕“指标—瓶颈—证据—根因”这条主线展开,而不是停留在工具操作层面。