问题
Android开发时可以通过AndroidStudio提供的一些系列工具查看应用的内存占用,十分的方便。
但是如果是对一个成品的已安装App快速查看内存占用呢,下面简单讲两种方式。
方案一:top 命令
top
命令是一个linux下的基础命令,相信熟悉 linux 的同学都会使用。android 作为 linux 内核的系统,也具备很多 linux 下的常用指令。
首先,通过 adb shell
进入到 android 设备的内置终端。
$ adb shell
gemini:/ $
然后,直接执行 top
命令即可看到系统中运行的进程所占用的内存
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 来过滤
top | grep 'com.xxx.xxx.xxx.xxx'
方案二(推荐):adb dumpsys meminfo
top
命令非常好用,但是在Android手机上,由于国产机的各种定制原因,这个命令不一定在每个设备上都会存在,所以我们还可以使用 adb dumpsys meminfo
命令来查看,这是个adb的通用命令。
adb shell dumpsys meminfo com.xxx.xxx
$ 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 TOTAL
和 Private Dirty
. 在某些情况下,Private Clean
和 Heap 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 Dirty
和Private 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
命令。
参考资料: