性能监控基础:Android系统性能指标简介

做APM平台,说白了就是给App做体检。你得先知道要量哪些指标,才能对症下药。我刚开始做性能优化时,一上来就抓CPU,结果发现帧率崩得一塌糊涂——内存泄漏早就把堆撑爆了。所以了解每个指标背后在讲什么,比直接上手写工具重要得多。

核心观点:性能指标不是孤立的。CPU高可能是内存频繁GC引起的,帧率低可能是主线程被文件读写卡住了。你要看到它们之间的关联。

CPU指标——App的“大脑负荷”

CPU指标直接反映你的代码到底在忙什么。我个人习惯先盯两个东西:CPU使用率和线程状态。

指标名称说明采集源
CPU使用率(整体/进程/线程)单位时间内CPU忙碌时间的占比/proc/stat, /proc/pid/stat
用户态 vs 内核态占比用户态高→业务逻辑重;内核态高→系统调用频繁/proc/pid/stat
CPU负载(Load Average)1/5/15分钟平均等待队列长度/proc/loadavg
上下文切换次数频繁切换说明线程设计不合理/proc/pid/status
CPU频率(Freq)降频时性能会明显下降/sys/devices/system/cpu

我记得有一次线上投诉App突然卡顿,排查发现某个第三方SDK每秒钟开一个线程去轮询定位。内核态CPU直接飙到40%,上下文切换次数吓死人。嗯,后来改成定时任务+单线程才解决。

一点技巧:看CPU使用率别只看整体百分比。我建议把用户态和内核态分开看。如果内核态占比超过40%,八成是I/O或系统调用太多。

内存指标——App的“储物间”

Android的内存管理很特殊——有Java堆、Native堆、共享内存等等。你问用户手机还剩多少内存,不如看你的App占了多少PSS(比例分摊大小)。

指标名称说明采集源
PSS / RSS / USSPSS最接近实际占用;RSS包含共享库的全部;USS为独占/proc/pid/smaps, ActivityManager
Java堆大小(Heap Size)Dalvik/ART分配的堆内存上限和已用Debug.getMemoryInfo()
Native堆大小C/C++层分配的内存,容易泄漏而难以察觉mallinfo(), /proc/pid/status VmRSS
GC次数及耗时GC频繁会导致卡顿,尤其是并发标记阶段Logcat GC标签, adb shell dumpsys meminfo
内存碎片 & 大对象分配Bitmap、ByteBuffer等连续内存分配失败会触发OOMDebug.getRuntimeStat()

我曾经遇到过一个诡异的问题:App在低端机器上频繁OOM,但PSS才200MB。后来用Native层工具一查,原来那个图片加载库每次都mmap一块1GB的虚拟地址空间——虽然物理页没分配,但系统以为你在抢内存,直接杀了你的进程。所以Native堆监控一定要做。

注意:不要只看Java堆!很多大型App的内存大头在Native堆(图片解码、视频、WebView)。我建议至少把PSS和Native堆两个值一起采。

帧率指标——用户最直接的感受

帧率不只是看FPS。60FPS只是及格线,关键是掉帧次数和帧耗时分布。你想想,用户一滑动就感觉卡,其实就是某一帧超过了16.6ms。

  • FPS(帧率):每秒渲染帧数,但平均值会掩盖掉帧
  • Jank(掉帧/卡顿):单帧耗时超过16.6ms的次数
  • FrameTime(帧耗时):从dispatch到swapBuffer的完整时间
  • Choreographer Callback:Input、Animation、Traversal三个阶段耗时
  • 掉帧类型:丢帧、冻帧、跳帧各有不同原因

我以前有个项目,FPS一直55左右,用户反馈却很差。加上FrameTime分布图一看:每3~5秒就出现一个200ms的“大尖峰”。原来是某个定时器在主线程里高频计算hash,一算就是几百毫秒。改成异步后,峰值消失,用户再也没说卡了。

电量指标——用户的续航焦虑

电量优化是最容易被忽视的。我做得少,但踩过的坑不少。关键指标其实不复杂。

指标名称说明采集源
电流/功率(mA / mW)瞬时的功耗值,需要硬件或BatteryStats支持/sys/class/power_supply/current_now
WakeLock持有时间长期持有WakeLock会让CPU无法睡眠adb shell dumpsys power
Alarm唤醒次数频繁设置alarm会打断深度睡眠adb shell dumpsys alarm
WiFi/数据网络流量后台网络传输会持续耗电/proc/net/xt_qtaguid/stats
Doze模式进入/唤醒App阻止Doze会显著增加耗电adb shell dumpsys deviceidle

有一次我的App在后台耗电排名居高不下。查了半天发现是定位SDK每30秒请求一次网络位置,而且持有WakeLock不让手机睡。后来改成「按需定位+一次性WakeLock」,耗电直接降了70%。

建议:电量监控最难的就是硬件数据获取(没有root或者POWER权限很难读到真实电流)。我一般先抓WakeLock和Alarm这两个软指标,它们跟耗电的关联度很高。

其他不可忽视的指标

除了上面四个大头,APM平台还得考虑:

  • I/O读写:频繁小文件读写会导致存储碎片、ANR。采集读/写字节数、操作次数。
  • 网络延迟与丢包:影响接口耗时与用户等待时长。
  • 线程数:线程过多导致上下文切换爆发,甚至OOM(每个线程默认栈大小1MB)。
  • 启动时间:冷启动/热启动时长,影响用户第一印象。
Android 系统性能指标关系图 性能监控指标体系 CPU 指标 内存指标 帧率指标 电量指标 使用率 负载 线程状态 频率 PSS/RSS GC频率 堆内存 碎片 FPS 掉帧数 帧耗时 Choreo.回调 电流 WakeLock Alarm 网络流量 这些指标之间互相影响 例如:频繁GC → CPU升高 → 掉帧 → 发热 → 降频 → 更卡 APM平台需要同时采集多个维度,才能定位真正的根因

看到这张图你可能就明白了:每个指标都不是独立存在的。CPU高了会导致发热降频,降频又拖慢帧率,帧率低了用户就骂娘。所以APM平台的核心不是单独看一个数字,而是把CPU、内存、帧率、电量这四个维度串起来分析。

我个人建议,先从帧率和内存入手,它们跟用户体验最相关。CPU和电量可以作为深度排查的辅助。下一阶段我们就会讲怎么用开源工具把这些指标抓出来。嗯,先把概念理清,动手才不会慌。