CPU占用指标解读:user / sys / iowait 等指标含义

做前台服务优化,你得先看懂 CPU 指标。不然你连瓶颈在哪都不知道。我个人习惯先盯住三个核心指标:user、sys、iowait。它们分别代表什么?跟 Compose 重组抖动又有什么关系?我们一个一个说清楚。

CPU 指标全景图 CPU 核心指标 user 用户态 · 业务代码执行 sys 内核态 · 系统调用开销 iowait IO 等待 · 磁盘/网络阻塞 irq / softirq 中断处理 · 硬件/软中断 idle 空闲时间 · CPU 未利用率 前台服务场景下:user 高 → 业务逻辑重,sys 高 → 系统调用频繁 iowait 高 → IO 阻塞影响 UI 响应,idle 高但卡顿 → 排查锁问题

user CPU:你的业务代码在跑

user CPU 指的是用户态占用的 CPU 时间。说白了,就是你写的业务代码在干活。计算、循环、JSON 解析、数据转换……全算在 user 里。

我在项目中遇到过,某个前台服务的 user CPU 突然飙到 80%。一查发现是后台任务在疯狂解析超大 JSON。你想想看,这种纯计算操作不涉及系统调用,也不等 IO,就是实打实地占着 CPU 不放。

user CPU 高会直接挤压 UI 线程的时间片。虽然 Compose 的重组在子线程也能跑,但渲染管线的主线程如果拿不到 CPU,掉帧就来了。

优化思路:减少重复计算,用缓存替代实时运算,耗时任务放到低优先级线程。我在项目里用 WorkManager 把定期全量排序改成了增量更新,user CPU 直接从 60% 降到了 15%。

sys CPU:系统调用的代价

每次你调用系统 API——文件读写、网络请求、Binder 通信、甚至 Log.d()——都会陷入内核态。这部分开销就是 sys CPU。

我有个经验:如果 sys CPU 长时间超过 30%,说明你的程序在频繁做系统调用,得警惕了。举个例子,用 OkHttp 做网络请求,每次请求都涉及 socket 读写和 binder IPC。请求频率一上去,sys CPU 就跟着涨。

在 Android 前台服务里,Binder 调用是 sys CPU 的主要来源。Service 和 Activity 通信、跨进程传数据,这些都会产生 sys CPU 开销。sys CPU 高对 Compose 重组的影响很直接——UI 线程自己也做系统调用(比如 vsync 同步),如果 sys 被其他线程占满了,UI 线程就得排队等。

怎么查?用 adb shell dumpsys cpuinfo 看整体,再用 top -H -p <pid> 看线程级 sys 占比。我曾经用 Perfetto 抓过一次 trace,发现一个后台线程每秒调用 200 次 System.currentTimeMillis(),累计 sys 开销高得离谱。

iowait:IO 阻塞的代价

iowait 是 CPU 等待 IO 完成的耗时。这个指标很有意思——CPU 其实很闲,但在等磁盘或网络把数据送过来。所以 CPU 利用率不一定高,但性能可能很差。

我记得有一次排查前台服务卡顿,CPU 总利用率只有 30%,但 iowait 占了 40%。一查发现是日志框架在同步写文件,每次写一行日志都要等磁盘 IO。你想想看,UI 线程偶尔也要写日志(比如上报关键事件),这一等就是几十毫秒,掉帧就这么来的。

iowait 高对 Compose 重组的影响很隐蔽。它不是 CPU 不够用,而是线程被 IO 阻塞了。重组任务排在线程池里,前面的 IO 操作不完成,后面的重组就得等着。

避坑指南:我曾经在一个项目里用 BufferedWriter 异步写日志,但忘记设置缓冲区大小,结果每次 flush 还是同步 IO。后来改成 kotlinx.coroutines 配合 Mutex 异步批量写入,iowait 降到了 5% 以下。

irq、softirq 与 idle

这几个指标平时不太起眼,但出了问题很头疼。

  • irq / softirq:硬件中断和软中断。一般占比很低,如果异常飙高,可能是驱动问题或网卡负载过重。我见过一台测试机因为 Wi-Fi 驱动 bug,softirq 占了 20% CPU,前台服务每 3 秒卡一次。
  • idle:空闲时间。idle 高说明 CPU 在摸鱼。但别高兴太早——如果 idle 高但服务依然卡顿,那大概率是锁竞争或线程调度出了问题。比如一个线程持有锁在等 IO,其他线程只能干等,CPU 看着很闲,但业务就是跑不动。

对于前台服务来说,irq/softirq 异常需要从硬件或驱动层面排查;idle 异常则需要用 Thread Dump 或 SimplePerf 看线程状态。

指标速查表

指标 含义 对前台服务的影响 优化方向
user 用户态 CPU 占用 业务逻辑重,抢占 UI 时间片 减少计算,异步化,缓存
sys 内核态 CPU 占用 系统调用频繁,UI 响应变慢 减少 Binder 调用,批量操作
iowait IO 等待耗时 IO 阻塞导致线程卡顿 异步 IO,缓冲区,无阻塞设计
irq/softirq 中断处理 异常时服务周期性卡顿 排查驱动,更新固件
idle 空闲时间 高但卡顿 → 锁或调度问题 检查锁竞争,优化线程模型

实战:怎么看这些指标

工具用对,事半功倍。我个人习惯先看整体,再查细节。

# 整体 CPU 负载(按进程汇总)
adb shell dumpsys cpuinfo

# 实时看指定进程的 CPU 占用
adb shell top -n 1 | grep 你的包名

# 看每个线程的 CPU 占用(前台服务场景)
adb shell top -H -p $(adb shell pidof com.yourapp)

# 用 Perfetto 抓 trace(最推荐)
adb shell perfetto -o /data/misc/perfetto-traces/trace -t 10s \
  --config '{"duration_ms":10000,"buffers":[{"size_kb":1024}],"data_sources":[{"config":{"name":"linux.sys_stats"}}]}'

拿到数据后,重点看 user + sys 的总和是否超过 50%,以及 iowait 是否长期 > 10%。如果 user 高,优先优化业务代码;sys 高,砍系统调用;iowait 高,改异步 IO。

我曾经踩过一个坑:用 dumpsys cpuinfo 看到 user 和 sys 都不高,以为没问题。结果用 Perfetto 一看,iowait 在特定场景下冲到 35%,刚好跟 UI 掉帧的时机完全吻合。所以,光看整体指标不够,还得结合时序 trace。

嗯,CPU 指标就这么回事。读懂它们,你就能精准定位前台服务的性能瓶颈。下一步就是针对性地优化——但那是后面章节的事了。