1、帧率与卡顿的基本概念
做性能优化这么久,我始终觉得:帧率和卡顿是监控工具最核心的两个概念。你连它们到底指什么都不清楚,后面调度定位根本无从下手。
1.1 帧率到底是什么
帧率,官方叫 FPS(Frames Per Second)。说白了就是每秒钟屏幕能刷出多少张画面。我记得刚入行时以为只要 FPS 高就万事大吉——后来被现实狠狠打了一耳光。
一个小例子:玩射击游戏时 FPS 显示 60,但开枪瞬间画面却像幻灯片。为什么?因为帧率只看平均,你忽略了一帧一帧的细节。
你看,单纯关注平均帧率是不够的。帧率有两个关键属性:
- 平均值:你通常看到的 30fps、60fps 就是这个
- 稳定性:每秒内部每帧的耗时波动有多大
1.2 卡顿的本质:帧时间超长
卡顿,技术上讲就是某一帧的渲染时间突然远超正常值。我项目中遇到最典型的情况:UI 滑动列表时,某帧从 16ms 飙升到 80ms,用户立刻感觉“卡了一下”。
这里有个核心公式:
帧时间(单帧耗时) = 1 / 帧率
举例来说:
| 目标帧率 | 理想帧时间 | 卡顿阈值(经验值) |
|---|---|---|
| 60 fps | 16.67 ms | > 33 ms |
| 30 fps | 33.33 ms | > 66 ms |
| 120 fps | 8.33 ms | > 16 ms |
重要:帧时间 不达标 才算卡顿。如果一个 60fps 的应用,偶尔掉到 55fps,人眼几乎无感;但如果某帧耗了 50ms,那就明显卡了。
1.3 卡顿的三大成因
我个人习惯把卡顿原因归为三类,你在写监控工具时一定要先拿这个框架去套:
- 渲染层:GPU 压力大、过度绘制、复杂动画等
- 调度层:主线程被长任务阻塞、锁竞争、GC 停顿
- IO 层:磁盘读写、网络请求堵塞 UI 线程
嗯,这里要重点提一下调度层——也是我们这门课真正要解决的问题。很多帧率监控工具只看渲染时间,却忽略了后台线程对主线程的干扰,导致定位不到根因。
1.4 理解帧率与卡顿的关系
你想想看,如果屏幕刷新率是 60Hz,系统每 16.67ms 发出一个 VSync 信号要求新画面。如果这一帧的渲染时间超过 16.67ms,那就丢帧了。连续几帧超时,你就感觉到卡顿。
这是我的知识结构图,看完你应该能对帧率和卡顿有整体认识:
1.5 怎么量化卡顿?常用指标
框架图里提到了几个卡顿指标,这里展开说说:
- 丢帧率:超出目标帧时间的帧数 / 总帧数 × 100%。我曾经用这个指标排查过一个电商列表页,卡顿率从 5% 优化到 0.2%
- Jank 次数:单次连续超时按 1 次 Jank 计数。Google 的 Perfetto 工具就用这个
- 帧时间 P99:排名 99% 位置的那一帧耗时。P99 超过 50ms 基本就算严重卡顿了
避坑! 我曾经只盯着平均 FPS,结果上线后用户投诉“滑动页面总是一顿一顿的”。后来一查,平均帧率 58fps,但 P90 帧时间高达 80ms——平均掩盖了真相。所以监控工具一定不能只看平均值。
1.6 实际工具里如何用这些概念
我们做帧率监控工具,第一步就是采集每帧的耗时数据。简单代码示意:
// 伪代码:采集一帧的耗时
long startTime = System.nanoTime();
doFrame(now) {
long frameDuration = now - previousFrameStartTime;
if (frameDuration > targetFrameTimeMs * 1e6) {
recordJank(frameDuration); // 记录卡顿事件
}
previousFrameStartTime = now;
}
嗯,这里要注意:targetFrameTimeMs 一般是 16.67(60fps)或 33.33(30fps)。你写工具时别写死,要支持动态配置。
好了,第一课就到这里。记住核心思想:帧率是表象,帧时间和卡顿指标才是本质。后面章节我们会一步步深入后台调度,定位到底谁在拖慢主线程。