第二章:性能度量指标——你到底该盯住哪个数?
做性能优化,最怕的是“凭感觉”。
我记得刚接手一个SDK接入项目时,大家只盯着帧率看——帧率一掉就觉得SDK有问题。结果排查了半天,发现是CPU占用率爆了,导致渲染线程饿死。从那以后,我定了个规矩:没有度量,就没有优化。
这一章,我们就把四个核心指标掰开揉碎。你不但要知道它们是什么,还得知道在什么场景下该盯哪一个。
2.1 CPU使用率——别被平均值骗了
CPU使用率,说白了就是你的代码占用了处理器多少时间片。这个指标我一般拆成两层来看:全局CPU占用率和本进程CPU占用率。
| 维度 | 关注点 | 典型阈值 | 监控方式 |
|---|---|---|---|
| 全局CPU | 设备整体负载 | 超过80%需警惕 | dumpsys cpuinfo |
| 进程CPU | 自身SDK消耗 | 超过20%需要优化 | top / /proc/pid/stat |
| 线程级CPU | 具体哪个线程在吃 | 单线程持续>40% | Traceview / Perfetto |
我在项目中遇到过一个问题:某个广告SDK在后台进行图片预加载,CPU占用从5%飙到了60%。但它的上报周期是1分钟一次,平均下来只有15%。嗯,这里要注意:只看平均值,你会漏掉峰值。
2.2 内存占用——不是越少越好
内存指标最容易被误解。很多人觉得“内存占用越低越好”,其实不是。内存本来就是拿来用的,关键是不要泄漏,不要碎片化。
// 重点关注三个维度
1. PSS(Proportional Set Size):进程实际占用的物理内存
2. Java Heap:Dalvik/ART堆的已用大小
3. Native Heap:C/C++分配的内存
我个人习惯在接入SDK后,用adb shell dumpsys meminfo [包名]打一份快照,记录基线。之后每次版本迭代都对比这个基线。一旦PSS增长了超过10%,就要立即排查。
为什么我会强调这个?有一次我们接入一个推流SDK,上线第一周一切正常。一个月后,部分用户出现OOM崩溃。一查,是SDK内部缓存了视频帧数据,但释放逻辑有漏洞——画面分辨率变化时,旧帧没清掉。这就是典型的隐性增长。
| 场景 | 关注点 | 建议监测手段 |
|---|---|---|
| 冷启动 | 初始化时一次性分配 | 记录启动结束时的PSS |
| 运行中 | 周期性垃圾回收 | GC日志 + 大对象分配检查 |
| 退出/销毁 | SDK是否释放所有资源 | 退出后PSS是否回到基线 |
2.3 帧率——别只看平均值,要看掉帧分布
帧率(FPS)是用户最直观能感受到的指标。但你可别傻傻盯着60FPS这个数字。
为什么?一个简单例子:你在1秒内前500ms卡了1帧(掉到30FPS),后500ms跑满了60帧。平均下来还是接近55FPS,看起来很美好。但用户感受就是“卡了一下”。
我建议你关注两个东西:帧分布直方图和掉帧堆栈。
- 帧分布直方图:看有多少帧落在8ms、16ms、24ms、32ms以上。
- 掉帧堆栈:在掉帧那一刻,主线程在做什么?
// 常用的帧监控工具
- Choreographer.FrameCallback(自己打桩)
- BlockCanary(卡顿定位)
- 系统自带的GPU渲染模式(开发者选项)
我记得有一次,某社交SDK在列表滑动时频繁掉帧。直方图显示大部分帧都在8ms以内,但每10帧会出现一个50ms的“毛刺”。一查堆栈,发现是SDK在访问本地数据库时走了主线程IO。这就是典型的“高帧率下的卡顿”——平均值好看,但体验差。
2.4 启动时间——首帧和首屏,不是一个概念
SDK接入后,最怕的就是拖慢App冷启动。你想想看,用户等了几秒才看到内容,第一印象就差了。
启动时间我习惯拆成两个阶段:
- 首帧时间:从Application.onCreate到第一个Activity的onResume完成。
- 首屏时间:用户看到完整内容的时间点。
| 阶段 | SDK常见影响 | 优化方向 |
|---|---|---|
| 首帧 | 在onCreate中执行网络请求、数据库初始化 | 异步初始化、懒加载、ContentProvider优化 |
| 首屏 | SDK等待服务端配置、加载广告素材 | 预缓存、按需加载、超时降级 |
2.5 把这些指标串起来
好了,现在你已经知道每个指标是什么了。但实际工作中,这些指标是相互关联的。我画了一张图,帮你理清它们之间的关系。
这张图告诉我们一个朴素的道理:CPU高了会拖累帧率和启动时间;内存泄漏了会导致频繁GC,进一步拉高CPU。做优化的时候,千万不要孤立地看一个指标。
我个人经验是:先解决内存问题,再从上往下调CPU,最后验证帧率和启动时间。这个顺序不一定放之四海皆准,但大多数场景下最高效。