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

4,200 阅读7分钟

问题

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 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 命令。

参考资料: