Android分析已安装应用占用内存

问题

Android开发时可以通过AndroidStudio提供的一些系列工具查看应用的内存占用,十分的方便。

但是如果是对一个成品的已安装App快速查看内存占用呢,下面简单讲两种方式。

方案一:top 命令

top 命令是一个linux下的基础命令,相信熟悉 linux 的同学都会使用。android 作为 linux 内核的系统,也具备很多 linux 下的常用指令。

首先,通过 adb shell 进入到 android 设备的内置终端。

1
2
$ adb shell
gemini:/ $

然后,直接执行 top 命令即可看到系统中运行的进程所占用的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
gemini:/ $ top
User 11%, System 47%, IOW 0%, IRQ 2%
User 4 + Nice 0 + Sys 16 + Idle 13 + IOW 0 + IRQ 1 + SIRQ 0 = 34

PID USER PR NI CPU% S #THR VSS RSS PCY Name
27123 shell 20 0 20% R 1 9112K 2148K fg top
578 system 12 -8 8% D 12 179908K 16532K unk /system/bin/surfaceflinger
1483 system 10 -10 2% S 205 2767360K 363172K ta system_server
2457 root RT 0 2% S 1 0K 0K fg irq/22-408000.q
3 root 20 0 2% S 1 0K 0K fg ksoftirqd/0
25889 root 20 0 2% S 1 0K 0K fg kworker/u8:6
13326 u0_a1179 10 -10 2% S 81 2473172K 326764K ta com.xxx.xxx.xxx.xxx
12 root 20 0 0% S 1 0K 0K fg ksoftirqd/1
15 root RT 0 0% S 1 0K 0K fg migration/2
16 root 20 0 0% S 1 0K 0K fg ksoftirqd/2
19 root RT 0 0% S 1 0K 0K fg migration/3
20 root 20 0 0% S 1 0K 0K fg ksoftirqd/3
23 root 0 -20 0% S 1 0K 0K fg khelper
24 root 0 -20 0% S 1 0K 0K fg netns
25 root 0 -20 0% S 1 0K 0K fg perf
26 root 0 -20 0% S 1 0K 0K fg smd_channel_clo
27 root 20 0 0% S 1 0K 0K fg dsps_smd_trans_
28 root 20 0 0% S 1 0K 0K fg lpass_smd_trans

以上 pid=u0_a1179 就是我们的游戏占用内存的情况, 简单解释下几个关键参数:

  • PID : 进程的id
  • VSS : Virtual Set Size 虚拟耗用内存(包括共享库占用的内存),即单个进程全部可访问的地址空间,器大小可能包括还尚未在内存中驻留的部分。对于确定单个进程实际内内存使用大小,VSS用处不大。
  • RSS Resident Set Size 实际使用物理存储(包括共享库占用的内存),即单个进程实际占用的内存大小,RSS不太准确的地方在于它包括该进程所使用的共享库全部内存大小。对于一个共享库,可能被多个进程使用,实际该共享库只会被装入内存一次。

通过上述解释,我们可以看出,游戏占用的内存大约是 326M 左右。

以上是查看所有内存,查看指定包名还可以使用 grep 来过滤

1
top | grep 'com.xxx.xxx.xxx.xxx'

方案二(推荐):adb dumpsys meminfo

top 命令非常好用,但是在Android手机上,由于国产机的各种定制原因,这个命令不一定在每个设备上都会存在,所以我们还可以使用 adb dumpsys meminfo 命令来查看,这是个adb的通用命令。

adb shell dumpsys meminfo com.xxx.xxx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
$ adb shell dumpsys meminfo com.xxx.xxx -d
Applications Memory Usage (in Kilobytes):
Uptime: 7921887 Realtime: 7921887

** MEMINFO in pid 20533 [com.game.win.wallet.demo] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 33297 33236 0 0 69632 42993 26638
Dalvik Heap 32595 32416 0 0 39529 23726 15803
Dalvik Other 1290 1288 0 0
Stack 2176 2176 0 0
Ashmem 134 132 0 0
Gfx dev 149106 147676 16 0
Other dev 25 0 24 0
.so mmap 34269 988 31444 0
.jar mmap 0 0 0 0
.apk mmap 435 0 156 0
.ttf mmap 907 0 788 0
.dex mmap 8601 8 8592 0
.oat mmap 2154 0 148 0
.art mmap 3655 2532 236 0
Other mmap 1235 8 1168 0
EGL mtrack 13824 13824 0 0
GL mtrack 30520 30520 0 0
Unknown 27758 27756 0 0
TOTAL 341981 292560 42572 0 109161 66719 42441

App Summary
Pss(KB)
------
Java Heap: 35184
Native Heap: 33236
Code: 42124
Stack: 2176
Graphics: 192036
Private Other: 30376
System: 6849

TOTAL: 341981 TOTAL SWAP PSS: 0

Objects
Views: 13 ViewRootImpl: 1
AppContexts: 4 Activities: 1
Assets: 7 AssetManagers: 3
Local Binders: 28 Proxy Binders: 29
Parcel memory: 20 Parcel count: 82
Death Recipients: 1 OpenSSL Sockets: 5

SQL
MEMORY_USED: 682
PAGECACHE_OVERFLOW: 202 MALLOC_SIZE: 62
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 20 12 0/15/1 /data/user/0/com.xxx.xxx/databases/jsb.sqlite
25/40/16 /data/user/0/com.xxx.xxx/databases/bd_embed_tea_agent.db
4 92 65 10/25/11 /data/user/0/com.xxx.xxx/databases/ttopensdk.db
4 20 12 0/15/1 /data/user/0/com.xxx.xxx/databases/npth_log.db
4 20 87 1/18/2 /data/user/0/com.xxx.xxx/databases/bytedance_downloader.db

下面解读一下这份数据里的关键数据。

首先了解两个概念:

  • 私有内存(Dirty and Clean) RAM:

    进程独占内存,也就是进程销毁时可以回收的内存容量。

    通常 Private Dirty 内存是最重要的部分,因为只被自己进程使用。

    Dirty 内存是已经被修改的内存页,因此必须常驻内存(因为没有swap)。

    Clean内存是已经映射持久文件使用的内存页(例如正在被执行的代码),因此一段时间不使用的话就可以置换出去。

    所有的 Dalvik Heap 和 Native Heap 都属于 Private Dirty,

    与 Zygote 进程共享的 Dalvik Heap和 NativeHeap 则是共享 Dirty.

  • 实际使用内存(PSS)

    将跨进程共享页也加入进来,进行按比例计算 PSS。这样能够比较准确的表示进程占用的实际物理内存。

    PASS衡量的一个优点是,你可以将所有进程的 PSS 加起来,确定所有进程占用的实际内存。这表示 PSS 是一种理想的方式,来衡量进程的 实际 RAM 占用比重,以及相对于其他进程和可用的总 RAM 而言,对 RAM 的占用情况。

通常我们需要关注的是 PASS TOTALPrivate Dirty . 在某些情况下,Private CleanHeap Alloc 列提供的数据也值得关注。下面列出了关于不同的内存分配(各行)需要关注的其他信息:

  • Dalvik Heap

    Dalvik 虚拟机分配的内存。PSS Total 包含所有 Zygote 分配使用的内存,共享跨进程加权。

    Private Dirty 是应用独占内存大小,包含独自分配的部分和引用进程从 Zygote 复制时被修改的 Zygote 分配的内存页。

    Heap Alloc 是 Dalvik Heap 和 Native Heap 分配使用的大小,它的值比 Pss Total 和 Private Dirty 大,因为进程是从 Zygote 中复制分裂出来的,包含了进程共享的分配部分。

  • .so mmap & .dex mmap …mmap

    这些 mmap 概括一句话就是:映射本地或虚拟机代码到使用的内存中。

  • .so mmap 和 .dex mmap

    映射的 .so(原生) 和 .dex(Dalvik 或 ART) 代码占用的 RAM 。 Pss Total 值包括应用之间共享的平台代码。

    Private Clean 是应用自己的代码,通常实际映射的内存容量要大的多。此处的 RAM 只是应用已执行的代码当前需要占用的 RAM。

    不过,.so mmap 具有较大的 Private Dirty RAM,这是因为在将其加载到最终地址时,对原生代码进行了修复。

  • .oat mmap

    这是Heap 映像(Image)占用的 RAM 容量,根据多个应用共用的预加载类计算。此映像(Image)在所有应用之间共享,不受特定应用影响。

  • .art mmap

    这是 Heap 映像(Image) 占用的 RAM 容量,根据由多个应用共用的预加载类计算,此映像(Image)在所有应用之间共享,不受特定应用影响。尽管 ART 映像(Image)包含 Object 实例,但它不会计入您的堆(Heap)占用空间。

  • Unknown

    系统无法将其分类到其他更具体的一个项目中的 任何 RAM 页。当前,此类 RAM 页主要包含原生分配,由于地址空间布局随机化(ASLR),工具在收集此数据时无法识别这些分配。与 Dalvik Heap(堆) 相同,Unknown 的 Pass Total 考虑了与 Zygote 共享的容量,且 Private Dirty 是仅由您的应用占用的未知的 RAM.

  • TOTAL

    您的进程占用的按比例分摊的内存大小(PSS)RAM 总容量,等于上述所有 PSS 字段的综合。该值表示了您的进程占用的内存容量占总体内存容量的币种,可以直接与其他进程和可用的总 RAM 进行比较。

    Private DirtyPrivate Clean 合起来就是您进程中的总分配,这些分配未与其他进程共享。这些分配(尤其是 Private Dirty)的容量等于进程销毁后将释放到系统中的 RAM 容量。Private RAM 页由于已经被修改过,因此必须保留在 RAM 中(因为没有swap)。

    Clean RAM 页是从某个持久性文件(例如正在执行的代码)映射而来的,因此如果暂时不适用,可以将其置换出 RAM.

  • ViewRootImpl

    您的进程中当前处于活动状态的根视图数量。每个根视图斗鱼一个窗口关联,因此该值有助于您确定与对话框或其他窗口有关的内存泄漏。

  • AppContexts 和 Activities

    您的进程中当前处于活动状态的应用 Context 和 Activity 对象数量,该值可以帮助您快速确定发生泄漏的 Activity 对象,这些对象由于存在对其的静态引用(比较常见)而无法进行垃圾回收。这些对象往往关联了许多其他分配,因此是查找大型内存泄露的理想工具。

通过 Pss Total 我们可以看出游戏当前占用内存大约是 341M 左右(跟top有浮动是因为游戏阶段不同)。

综上所述,推荐使用 adb dumpsys mem 的方式查看内存占用,熟悉Linux但对Android不太熟悉的,可以尝试用 top 命令。

参考资料:

文章作者: 普通程序员
文章链接: https://programmerauthor.github.io/2020/03/24/android-mem/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 普通程序员