4. 性能数据采集原理:Proc文件系统,dumpsys,trace等

做APM平台,第一步就是搞定数据来源。我最早接触这块时,以为只是调几个API就行。后来踩了不少坑,才发现底层采集的水挺深。嗯,今天咱们就好好聊聊三大主流采集方式:Proc文件系统、dumpsys命令、Trace机制。

一句话总结:Proc拿原始系统状态,dumpsys取服务快照,Trace抓时序事件流。三者互补,缺一不可。
APM 数据采集三层架构 Proc 文件系统(系统级原始指标) /proc/stat /proc/meminfo /proc/[pid]/stat /proc/[pid]/io dumpsys 命令(服务状态快照) meminfo cpuinfo battery activity Trace 机制(时序事件流) Systrace(已废弃) atrace Perfetto 数据流向:底层→上层

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
⚠️ 我曾经踩过的坑: 每秒轮询一次 /proc/stat 本身就会产生少量CPU开销。在低端机上,如果还同时读 /proc/[pid]/io 这种文件,会发现采集线程自己都占了好几个百分点。解决办法:
– 采样间隔不要小于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机制前后有三个版本:

  1. Systrace(旧版,Android 9之前):基于ftrace,输出HTML文件。功能简单,现在基本废弃了。
  2. atrace(内置shell命令):是Systrace的后端,可以直接执行 atrace --async_start 和 atrace --async_stop 来采集。我早期APM就是用这个方案,因为不需要额外权限。
  3. 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版本也有差异。我的经验是:永远先在一台实机上试跑三次,再封装代码。