[小笔记] 如何解读 dumpsys meminfo 的信息
通过 adb 命令 adb shell dumpsys meminfo [pakcage name] 能够很简单的看到对应应用的内存使用总体情况,这里面的信息我自己也很容易忘记,我也在网上查找过许多的资料,我发现很多都是写得乱七八糟的,所以我决定自己针对官方的文档和 StackOverflow 中的优秀回答来记录一下。
这是我的应用使用上述命令后展示的信息(不同的 Android 版本,展示的信息可能有区别):
yaml
Applications Memory Usage (in Kilobytes):
Uptime: 76374295 Realtime: 369954849
** MEMINFO in pid 15901 [com.phx.waha.dev] **
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 77711 77684 0 23 79112 105984 88719 17264
Dalvik Heap 13888 13788 0 77 15364 17353 8677 8676
Dalvik Other 3702 2980 0 0 5416
Stack 1908 1908 0 0 1920
Ashmem 55 12 0 0 504
Other dev 48 0 44 0 632
.so mmap 43724 2460 35820 15 79656
.jar mmap 6071 0 3344 0 37364
.apk mmap 698 0 232 0 2288
.ttf mmap 1047 0 264 0 2640
.dex mmap 12702 512 12180 0 13428
.oat mmap 52 0 4 0 1744
.art mmap 8602 7948 92 43 19540
Other mmap 112 40 64 0 908
GL mtrack 67368 67368 0 0 67368
Unknown 1277 1256 8 0 1688
TOTAL 239123 175956 52052 158 329572 123337 97396 25940
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 21828 34904
Native Heap: 77684 79112
Code: 54836 138068
Stack: 1908 1920
Graphics: 67368 67368
Private Other: 4384
System: 11115
Unknown: 8200
TOTAL PSS: 239123 TOTAL RSS: 329572 TOTAL SWAP PSS: 158
Objects
Views: 53 ViewRootImpl: 2
AppContexts: 7 Activities: 2
Assets: 28 AssetManagers: 0
Local Binders: 31 Proxy Binders: 63
Parcel memory: 14 Parcel count: 55
Death Recipients: 3 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
我主要分析 MEMINFO 中的横坐标和纵坐标表示意思(有的我也不知道什么意思😂,我就列举下我知道的),其他的部分都很好理解我就不说了。
纵坐标
Pss Total
它的全称是 Proportional Set Size。首先我们要知道我们的应用中内存中的数据可以分为共享的和私有的。比如说系统中所有进程都能够用的资源文件就属于共享的(Shared);我们自己应用的分配的内存,别的应用无法读取的就是私有的(Private)。PSS 就是用来统计内存占用的一种方式,PSS = 私有内存占用 + 共享内存占用 / 共享进程数,通常我们也都使用 PSS 来表示总体的内存占用;还有一种统计内存占用的方式就是 RSS,RSS = 私有内存占用 + 共享内存占用,通常我们不会用 RSS 来表示总体的内存占用,只是做一个参考。
Private Dirty / Clean
Private 的内存占用仅供我们自己的进程使用,当进程销毁时系统可以回收 Private 中的绝大部分内存 (我不清楚为什么不是全部🤔),通常最重要的部分就是 Private Dirty 部分,我们应用请求分配的内存也都是这部分,它的占用也是最大的,Android 系统中默认不支持 Linux Swap 机制,它不能保存到磁盘上。所有的 Dalvik 虚拟机和 native 分配的堆内存都是 Private Dirty 的。
官方的文档没有明确表示什么样的内存是 Dirty 的,什么样的内存是 Clean 的,我在 StackOverflow 中看到有人回答了这个问题。
Private Dirty
这部分内存用于我们计算,内存中的内容也是可以修改的,我们自己写的代码分配的内存都是这部分内存。他们也不会被 Swap,当我们的进程被销毁后,这部分内存会被全部回收。
Private Clean
这部分内存从磁盘加载到内存中后就不会修改,比如我们的 dex 字节码和 native 机器码,后续的 .dex mmap 和 .so mmap 行中的内容就能够很容易看到这一点。
从 Zygote 进程 fork() 后没有修改的数据也是 Clean 的,如果被修改了就会变成 Dirty。
SwapPss Dirty
Android 官方文档也没有对这部分内存解释,以下解释都来自 StackOverflow。
前面说到 Android 默认不支持 Linux 的 Swap 机制,这个 Swap 机制是指 Swap 到本地磁盘存储。不懂 Swap 可以看看我之前写的文章:聊聊虚拟内存。这里是类似于 Linux 中的 ZRAM 机制,当内存不足时会把一部分内存压缩交换到 Compressed Page 中,以便节省内存,当需要这部分内存时再把他解压后重新加载到对应的 Page 中。
Rss Total
前面讲 PSS 的时候有讲到,只是一种统计内存的方式:RSS = 私有内存占用 + 共享内存占用。
Heap Size
堆的大小。
Heap Alloc
已经分配的堆的大小。
Heap Free
空余的堆大小,计算方式为 Heap Free = Heap Size - Heap Alloc。
横坐标
Native Heap
Native 分配的堆内存占用大小,这里有一个让人很费解的点,为什么 Heap Alloc 占用的内存要大于 PSS 呢?是因为这部分内存包含 Zygote fork() 前的共享数据,所以比 PSS 要大,但是为什么比 RSS 还大我就不知道原因了,官方没有讲,可能是他们的计算方式不一样。
Dalvik Heap
Dalvik 虚拟机堆内存占用大小,其他的都是和 Native Heap 类似的。
Dalvik Other
主要是 Dalvik 虚拟机的 JIT 和 GC 占用的内存。
Stack
方法栈占用内存。
.so mmap
native 机器码所占内存,我们能够看到大部分的占用是 private clean 的,也就是不修改的私有内存。
.dex mmap / .jar mmap
dex 字节码占用内存和 jar 字节码占用内存,和 .so mmap 类似。
.ttf mmap
字体文件占用内存,感兴趣的可以去了解了解 FreeType。
GL mtrack
Open GL 图像相关占用内存,和后面的 Graphics 对应。
还有一些其他的参数就不说了,有的都比较简单,非常直白,有的我也没有搞懂,你可以自己再去查查。
总结
通常我们看内存分析也只是看关键的几个信息,我们先从纵坐标来开始看:PSS,Private Dirty,Heap Size,Heap Alloc 和 Heap Free。从横坐标来看:Native Heap,Dalvik Heap 和 GL mtrack。主要通过以上指标我们就能知道内存大体的占用情况,其他指标也可以参考一下,如果想要看 Heap 中具体的内存占用,还需要借助别的工具,比如说 Android Studio 中的 Profiler。