1.4.2 smaps
由于Android底层基于Linux内核,进程内存信息也和Linux一致,所以Dalvik Heap之外的信息都能够从/proc/<pid>/smaps中取得。
在smaps中,列出了进程的各个内存区域,并根据分配的不同用途做标识,以下是root用户使用cat /proc/<pid>/smaps的一个例子:
788c2000-789bf000 rw-p 00000000 00:00 0 [stack:5113]
Size: 1012 kB
Rss: 4 kB
Pss: 4 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 4 kB
Referenced: 4 kB
Anonymous: 4 kB
AnonHugePages: 0 kB
Swap: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
VmFlags: rd wr mr mw me nr
dumpsys统计各个内存块的Pss、Shared_Dirty、Private_Dirty等值,并按以下原则进行了归并:
/dev/ashmem/dalvik-heap和/dev/ashmem/dalvik-zygote归为Dalvik Heap。
其他以/dev/ashmem/dalvik-开头的内存区域归为Dalvik Other。
Ashmem对应/dev/ashmem/下所有不以dalvik-开头的内存区域。
Other dev对应的是以/dev下其他的内存区域。
文件的mmap按已知的几个扩展名分类,其余的归为Other Mmap。
其他部分,如[stack]、[malloc]、Unknown等。
了解了dumpsys的方法后,我们可以自己解析smaps,看看归并前各项的内存都是多少。这样能够得到比dumpsys更详细的信息,有助于分析一些问题。
首先将Pss分为以下几大类,计算各部分占比。在这个例子里,几大项是三分天下的节奏。Dalvik和Other dev内存都占了30%以上,剩下的是mmap和Unknown。进行内存优化时不能只看Dalvik部分,需要同时评估所有的部分。
1. Dalvik
Dalvik内存分为多个区域,meminfo统计的是所有区域累加的值:
Dalvik_Heap 5529
+ /dev/ashmem/dalvik-heap (deleted) 4680
+ /dev/ashmem/dalvik-zygote (deleted) 849
Dalvik_Other 3240
* LinearAlloc 1229
* Accounting 1579
* Code_Cache 432
+ /dev/ashmem/dalvik-LinearAlloc (deleted) 1229
+ /dev/ashmem/dalvik-aux-structure (deleted) 1291
+ /dev/ashmem/dalvik-bitmap-2 (deleted) 192
+ /dev/ashmem/dalvik-card-table (deleted) 96
+ /dev/ashmem/dalvik-jit-code-cache (deleted) 432
其中:
Dalvik_Heap—包括dalvik-heap和dalvik-zygote。堆内存,所有的Java对象实例都放在这里。
LinearAlloc—包括dalvik-LinearAlloc。线性分配器,虚拟机存放载入类的函数信息,随着dex里的函数数量而增加。著名的65535个函数的限制就是从这里来的。
Accounting—包括dalvik-aux-structure、dalvik-bitmap、dalvik-card-table。这部分内存主要做标记和指针表使用。dalvik-aux-structure随着类及方法数目而增大,dalvik-bitmap随着dalvik-heap的 增大而增大。
Code_Cache—包括dalvik-jit-code-cache。jit编译代码后的缓存,随着代码复杂度的增加变大。
由于堆内存部分往往是应用消耗内存最多的地方,在内存优化中,最常见的方法就是减少Dalvik Heap中创建的对象,能够直接减少Dalvik Heap,并间接减少Accounting部分。减少代码会直接减少运行辅助部分。
在进行不同版本的对比测试时,我们往往会发现Dalvik Other和Dex Mmap出现了稳定的增长,这是由新加入的代码引入的内存消耗。
根据Dalvik虚拟机的原理,在加载class时,会根据类的变量个数及函数个数申请相应大小的内存,作为运行时的内部指针。这部分内存就会体现在LinearAlloc及aux-structure的增长中。随着版本的开发,应用class的数目及复杂度也在不断地增长,因此Dalvik Other部分也在不断地增长。
由于这部分内存的增长取决于代码复杂度,因此通常情况下并没有简单直接的方法能够降低它们的消耗。但是通过仔细分析它们的组成及原理,还是能够找出一些间接的方法降低这部分内存的,详细方法请见2.6节。
2. mmap
系统会将一些文件mmap到内存中,对各个文件进行mmap的时机及大小比较复杂。dex_mmap是其中主要的内容:
apk_mmap 648
dex_mmap 1448
+ /data/dalvik-cache/data@app@com.example-2.apk@classes.dex 917
+ /system/app/Stk.odex 16
+ /system/app/TelephonyProvider.odex 140
+ /system/framework/android.policy.odex 8
+ /system/framework/bouncycastle.odex 2
+ /system/framework/conscrypt.odex 3
+ /system/framework/core.odex 50
+ /system/framework/ext.odex 19
+ /system/framework/framework.odex 249
+ /system/framework/framework2.odex 44
jar_mmap 4
ttf_mmap 47
so_mmap 3127
other_mmap 11
应用的dex会占据较大的空间,并且随着代码增加使得dex文件变大,占用的内存也会增加。减小dex的(相当于减少代码)尺寸能够降低这部分内存占用,同时也会减少dalvik部分的内存。