4. 性能数据采集原理:Proc文件系统,dumpsys,trace等
做APM平台,第一步就是搞定数据来源。我最早接触这块时,以为只是调几个API就行。后来踩了不少坑,才发现底层采集的水挺深。嗯,今天咱们就好好聊聊三大主流采集方式:Proc文件系统、dumpsys命令、Trace机制。
4.1 Proc文件系统:性能数据的“地基”
你想想看,Linux内核把系统几乎所有运行状态都暴露在/proc下了。CPU使用率、内存占用、进程线程数、磁盘IO……全藏在那些文本文件里。我当年第一次手动cat /proc/stat时,发现只是一串数字,完全看不懂。后来才明白,这些数字就是APM的“原油”。
常用的Proc节点,我整理了个表,你直接收藏就好:
| 节点路径 | 含义 | 主要字段 |
|---|---|---|
/proc/stat |
CPU时间累计(用户态、内核态、空闲等) | cpu行:user, nice, system, idle, iowait, irq, softirq, steal |
/proc/meminfo |
系统内存详情 | MemTotal, MemFree, Buffers, Cached, SwapTotal, SwapFree |
/proc/[pid]/stat |
进程状态、CPU时间、优先级等 | 字段3:state, 字段14:utime, 字段15:stime |
/proc/[pid]/io |
进程IO统计(读写字节、次数) | read_bytes, write_bytes, cancelled_write_bytes |
/proc/self/status |
当前进程的状态、内存、线程数 | Name, State, Threads, VmRSS, VmSize |
– 采样间隔不要小于200ms,保守点1s;
– 用批量读+缓存策略,别每次都打开关闭文件句柄;
– 对 /proc/stat 做差分计算出瞬时CPU使用率,而不是读原始数值。
4.2 dumpsys:系统服务的“万能钥匙”
说白了,dumpsys就是Android给开发者预留的后门。你执行 adb shell dumpsys meminfo com.example.app,就能拿到这个App的PSS、私有内存、共享内存等关键指标。我个人习惯把它当成“服务级体检仪”,因为Proc只能看到原始系统数据,而dumpsys能直接告诉你每个服务内部的状态。
常用的dumpsys子命令:
dumpsys meminfo– 进程内存详细分布(Java Heap、Native Heap、代码、栈、图形等)dumpsys cpuinfo– 各进程CPU使用率(注意:这个命令本身会触发一次CPU统计,有轻微开销)dumpsys battery– 电池状态、电量、温度、电流dumpsys activity– Activity栈、进程LRU列表dumpsys diskstats– 磁盘读写统计(Android 8.0+)
但是!dumpsys不是免费的午餐。我记得有一次线上app频繁调用dumpsys meminfo,结果被系统判定为“后台进程频繁查询”而杀掉了。后来我们改成只在用户感知卡顿时才触发采集,才解决问题。
– 优先使用
dumpsys -t 3 meminfo 设置超时(防止服务无响应卡住);– 对于非系统应用,dumpsys meminfo只能查到自己的信息,其他应用进程需要root或系统签名;
– 解析dumpsys输出建议用正则或逐行扫描,别用String.split("\n"),会遇到很大的内存峰值。
4.3 Trace:精准捕捉性能瓶颈
好,前面两种方式都是采“静态快照”。那要排查为什么一启动就卡顿、或者列表滑动掉帧怎么办?这就需要Trace了。你可以把Trace看作“带时间戳的日志流”,它记录了每个线程在什么时候执行了哪个函数、花了多久。
Android官方提供的Trace机制前后有三个版本:
- Systrace(旧版,Android 9之前):基于ftrace,输出HTML文件。功能简单,现在基本废弃了。
- atrace(内置shell命令):是Systrace的后端,可以直接执行
atrace --async_start和atrace --async_stop来采集。我早期APM就是用这个方案,因为不需要额外权限。 - Perfetto(Android 10+官方推荐):新一代性能分析套件,支持更精细的trace point、SQL查询、甚至内存剖析。生产环境我建议用Perfetto客户端API自定义trace。
怎么接入呢?代码里加一个Trace.beginSection("xxx")和Trace.endSection(),框架就会自动记录。但注意一点:嵌套层数太多会影响性能,尤其是在UI线程。
// 典型用法:在列表滑动时记录耗时
class MyAdapter extends RecyclerView.Adapter {
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Trace.beginSection("bindViewHolder#" + position);
try {
// 你的绑定逻辑
} finally {
Trace.endSection();
}
}
}
我曾经在生产环境加过100多个trace点,结果发现某些低端机因为trace buffer太大导致频繁GC。后来只保留了关键路径(启动、列表、网络请求),才把开销压下去。
– Proc文件系统:实时、轻量、适合持续监控;
– dumpsys:一次采集内容多、适合触发式诊断;
– Trace:精度高、信息全、适合卡顿场景深度分析。
三者配合使用,才能搭建一个不遗漏任何瓶颈的APM采集层。
嗯,数据采集原理就聊到这儿。实际动手时,你会发现每个子系统都有一些“坑”。比如Proc文件在某些内核版本里字段顺序变过,dumpsys输出格式不同Android版本也有差异。我的经验是:永远先在一台实机上试跑三次,再封装代码。