统计 Android 应用的 CPU 和内存

Android 系统基于 Linux 的内核构建起来,因此统计应用消耗的内存和占用的 CPU 时非常方便,本文介绍统计 CPU 和内存的命令,以及实现自动化统计的思路

CPU 的统计

Linux 提供了非常简单的一个命令 top,可以查看应用的 CPU 和内存占用情况,Android shell 下也可以直接使用它,输入如下命令就能够在窗口中循环输出 CPU 占用靠前的应用进程

$ adb shell top
400%cpu  67%user   7%nice  63%sys 251%idle   1%iow   9%irq   2%sirq   0%host
  PID USER         PR  NI VIRT  RES  SHR S[%CPU] %MEM     TIME+ ARGS
 2400 system       20   0 4.1G  64M  37M S 39.3   1.7   0:45.45 com.example+
 1050 system       18  -2 4.3G 138M  94M S 15.0   3.8   0:40.05 system_server
 3464 u0_a39       20   0 2.0G 238M  59M S 14.6   6.5   0:32.29 com.abc.test+

第 9 行 [%CPU] 和 第 10 行 %MEM 则分别对应进程的 CPU 占用率百分比和内存占用率百分比

这里 CPU 所在的列被中括号框选住,表示排序方式按 CPU 从高到低降序排列

另外,留意第一行第一列,显示的是 400%cpu,这是什么意思呢?

这表示 CPU 核心数不止一个,在这里有四个,所以就是 4 * 100%

想要查询这个命令传递参数的用法,可以输入

$ adb shell top --help
usage: top [-Hbq] [-k FIELD,] [-o FIELD,] [-s SORT] [-n NUMBER] [-d SECONDS] [-p PID,] [-u USER,]

Show process activity in real time.

-H	Show threads
-k	Fallback sort FIELDS (default -S,-%CPU,-ETIME,-PID)
-o	Show FIELDS (def PID,USER,PR,NI,VIRT,RES,SHR,S,%CPU,%MEM,TIME+,CMDLINE)
-O	Add FIELDS (replacing PR,NI,VIRT,RES,SHR,S from default)
-s	Sort by field number (1-X, default 9)
-b	Batch mode (no tty)
-d	Delay SECONDS between each cycle (default 3)
-n	Exit after NUMBER iterations
-p	Show these PIDs
-u	Show these USERs
-q	Quiet (no header lines)

这里面重点提示一下 -p 参数,表示只输出指定进程 ID,-H 参数,表示输出线程的占用率

我们发现,这个命令只是把结果打印到窗口里,如果要保存到文件中做进一步分析,或者提供给开发排查问题,可以输入

$ adb shell top > file.txt

这样就保存到文件中了

另外,上面提到默认情况下是按 CPU 降序排列,使用 -s 列号 的参数可以按照其他列进行降序排列,比如按内存占用降序

$ adb shell top -s 10

Android 中,除了 top 命令外,还有一个命令用来统计最近一段时间内的 CPU 占用率,这在快速分析最近 CPU 占用时非常有用

$ adb shell dumpsys cpuinfo
Load: 7.88 / 8.13 / 5.81
CPU usage from 372745ms to 72694ms ago (2018-10-18 20:10:37.725 to 2018-10-18 20:15:37.776):
  39% 2400/com.xxx.xxx.xx: 29% user + 10% kernel / faults: 708419 minor
  15% 1050/system_server: 9.6% user + 5.5% kernel / faults: 29353 minor
  13% 1863/com.android.phone: 9.5% user + 4.1% kernel / faults: 113330 minor
  13% 3464/com.yy.yyy.yyyy: 8.5% user + 4.7% kernel / faults: 24530 minor

从输出内容中,可以读到,这是一段 20:10:37.725 到 20:15:37.776 这五分钟内的 CPU 占用率统计

这段时间里,占用最高的应用占比为 39%,它的包名是 com.xxx.xxx.xx

其中 29% 为用户态,10% 为内核态

CPU 占用率统计的原理是什么呢?我这里以 Android 8.1 系统来解读一下

每个应用启动以后,会随机分配到一个 ID 值,叫做 PID,可以通过 ps 命令查询应用的包名时得到

$ adb shell ps |grep com.android.settings
system        4603   543 4378288  43428 0                   0 S com.android.settings

这里第二列的数字就是 PID,4603

应用启动以后,需要 CPU 做运算时,就会占用 CPU 的时间片让它去替自己做事,系统就会在某个位置写入应用的 CPU 占用时间,要查看这个时间,可以输入命令,以下命令中的 4603,需要替换为实际情况下的 PID 值

$ adb shell cat /proc/4603/stat
4603 (ndroid.settings) S 543 543 0 0 -1 1077936448 8978 0 13 0 4 6 0 0 20 0 11 0 5699 4483366912 10857 18446744073709551615 1 1 0 0 0 0 4612 0 1073775864 0 0 0 17 3 0 0 0 0 0 0 0 0 0 0 0 0 0

其中第 14 和 15 列的数字(上面的例子中分别为 4 和 6 ),分表表示进程在用户态下的 CPU 时间以及在内核态下的 CPU 时间
第 16 列和 17 列的数字(上面的例子中都是 0 ),分别表示进程的所有已经死亡线程在用户态和内核态下的 CPU 时间

依据是 LInux 系统 proc 描述

http://man7.org/linux/man-pages/man5/proc.5.html

这四个数求和便是进程从启动到现在,一共使用的 CPU 时间,它包括了该进程下所有线程的 CPU 时间总和

想要求得某个时间段中 CPU 的占用时间,则在 T1 时间,抓一次此数值,经过一段时间后,在 T2 时间,再抓一次此数值,求 T2 和 T1 数值之差便得到该时间段内的占用时间

既然我们是计算应用的 CPU 占用率,那么从哪里获得系统的占用时间呢?答案如下

$ adb shell cat /proc/stat
cpu  227074 29957 236622 747320 1822 34855 6375 0 0 0
cpu0 67340 7410 77242 142887 526 21616 4121 0 0 0
cpu1 64458 8939 77368 157790 717 9491 1260 0 0 0
cpu2 53326 7030 40617 216464 321 2078 615 0 0 0
cpu3 41950 6578 41395 230179 258 1670 379 0 0 0
...

对于每个数字的含义,仍然可参考上述页面下 Linux 对 proc 的解释

这里将输出结果第一行 cpu 字段后面的数字求和,比如上面例子中的:227074 29957 236622 747320 1822 34855 6375 0 0 0
即得到当前时间系统 CPU 的总时间片 SUM

分别抓取 T1 时刻和 T2 时刻系统 CPU 的 SUM1 和 SUM2,求差 SUM2 - SUM1 就得到这个时间段内整个系统对 CPU 的占用 SUM

接下来,由于数字中第 4 位表示 IDLE 状态时间片,求差得到是 IDLE2 - IDLE1 = IDLE

那么我们就可以算出来应用在 T1 和 T2 时间段内的 CPU 占用率为(SUM-IDLE)/ SUM

另外这里要多提一点,现在的设备几乎都是多核 CPU,上面的计算结果,还需要乘以 CPU 核心数,才是最终的占用率

内存的统计

内存的统计原理相比于 CPU 更加复杂,这里不展开来谈,我们先学习统计的方法

Android 中推荐使用 dumpsys 命令去统计内存,而不是使用 top,因为它们计算方式上略有差别,我们依据 Android 官方推荐为准,使用如下命令

$ adb shell dumpsys meminfo

从这个命令中,我们能提取到总的物理内存值 Total RAM: 3,706,588K (status normal)

如果启动的应用非常多,那么此命令有可能在默认超时时间 10 秒内统计不完,就会报错退出

增加一个超时参数 -t 秒数,就可以避免这种情况

$ adb shell dumpsys -t 30 meminfo

由于多数情况下,我们更关心某个应用的内存占用,可以指定它的包名或者 PID,去针对性的获得结果

$ adb shell dumpsys meminfo com.android.settings
# 或者
$ adb shell dumpsys meminfo 4603
** MEMINFO in pid 4603 [com.android.settings] **
                   Pss  Private  Private  SwapPss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap     1318     1276        0        0     6144     3154     2989
  Dalvik Heap      166      148        0        0     1466      954      512
 Dalvik Other      137      136        0        0
        Stack       40       40        0        0
       Ashmem        0        0        0        0
    Other dev        8        0        8        0
     .so mmap      156       64        0        0
    .apk mmap     2148        0     2048        0
    .dex mmap      900        8      820        0
    .oat mmap       75        0        0        0
    .art mmap      477      356        0        0
   Other mmap       10        4        0        0
      Unknown      506      504        0        0
        TOTAL     5941     2536     2876        0     7610     4108     3501

提取 Pss Total 列的 TOTAL 值即可

通过不带包名的命令获得总的内存值,就可以计算占用率了

另外要提醒的是,上述命令执行过程中会消耗一定的 CPU,如果正在统计 CPU,则尽量减少对内存的统计,以免对总 CPU 造成影响

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值