Android提供了一个名为meminfo的小工具帮助应用分析自身的内存占用,并且在4.4还新增了memtrack HAL模块,SoC厂商通过实现memtrack模块,让meminfo可以获取GPU相关的一些内存分配状况。了解meminfo的实现,对我们更深入了解应用的内存占用状况是很有帮助的。而这篇文章的目的就是分析Android 4.4 meminfo的内部实现源码,让开发者通过这些信息可以更了解自己应用的内存占用状况。
在控制台输入命令”adb shell dumpsys meminfo YOUR-PACKAGE-NAME”,可以看到类似下图的结果:
实际的调用代码入口在android.os.Debug.java和对应的CPP文件android_os_Debug.cpp,Debug.java的getMeminfo方法实际上调用了android_os_Debug.cpp的android_os_Debug_getDirtyPagesPid方法。
从上面的代码可以看到,android_os_Debug_getDirtyPagesPid方法先调用了load_maps方法,而load_maps方法要做的事情也很简单,它打开/proc/PID/smaps虚拟文件,读取里面的信息,在已ROOT的设备上,我们可以通过“adb shell cat /proce/PID/smaps”直接将这个虚拟文件的信息打印在控制台上。
“adb shell cat /proce/PID/smaps”输出的信息如上图所示,它实际上是应用的userspace地址空间的内存分配表,记录了应用分配的每一块内存的地址,类别,大小等信息,而load_maps方法调用read_mapinfo方法从这个表里面读出每一块内存的分配信息,分类进行累加,得出Native Heap,Dalvik Heap等各个类别的内存占用。
但是应用所使用的全部内存里面,有一些内存块是不映射到进程的userspace地址空间的(主要是GPU所使用的内存),这些内存块的信息在smaps里面无法找到,所以在Android 4.4里面新增了一个memtrack的HAL模块由SoC厂商实现,如果SoC厂商实现了memtrack模块,meminfo则可以通过libmemtrack的调用获取一些跟GPU相关的内存使用信息。所以我们看到android_os_Debug_getDirtyPagesPid方法通过调用read_memtrack_memory方法来读取Graphics,GL这两项的内存使用信息。
read_memtrack_memory方法的实现如上图所示,它读取了Graphics,GL,Other这三类内存信息,而这三个类别的定义在hardware/memtrack.h里面。
Graphics对应了MEMTRACK_TYPE_GRAPHICS,GL对应了MEMTRACK_TYPE_GL,而Other实际上是MEMTRACK_TYPE_OTHER,MEMTRACK_TYPE_MULTIMEDIA,MEMTRACK_TYPE_CAMERA这三项之和。memtrack是由SoC厂商实现的,在AOSP的源码里面我们可以找到高通的实现源码,在msm8974/libmemtrack/kgsl.c里面。
kgsl_memtrack_get_memory是memtrack的getMemory方法的具体实现,我们可以看到它实际上是读取一张内部的GPU内存分配表的信息(虚拟文件/d/kgsl/proc/PID/mem),在已ROOT的设备上,我们可以通过“adb shell cat /d/kgsl/proc/PID/mem”将这张内存分配表的信息打印到控制台上,如下图所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
gpuaddr
useraddr
size
id
flags
type
usage
sglen
7565e000
00000000
4096
1
--
--
p
gpumem
arraybuffer
1
756bc000
00000000
65536
2
-
r
--
p
gpumem
command
16
756cd000
00000000
65536
3
-
r
--
p
gpumem
command
16
756de000
00000000
65536
4
-
r
--
p
gpumem
command
16
756fb000
00000000
4096
5
--
--
p
gpumem
gl
1
75fe2000
00000000
262144
6
--
--
p
gpumem
gl
64
76023000
00000000
8192
7
--
--
p
gpumem
gl
2
76026000
00000000
8192
8
--
--
p
gpumem
gl
2
76029000
00000000
4096
9
--
--
p
gpumem
texture
1
.
.
.
94d71000
00000000
131072
362
--
--
p
gpumem
vertexarraybuff
32
94da0000
00000000
667648
176
--
l
-
p
gpumem
texture
163
94e44000
00000000
131072
363
--
--
p
gpumem
any
(
0
)
32
94e65000
00000000
131072
364
--
--
p
gpumem
any
(
0
)
32
c0000000
00000000
17268736
31
--
L
--
ion
egl
_image
4216
c1100000
00000000
8257536
36
--
L
--
ion
egl
_surface
21
c1900000
00000000
8257536
164
--
L
--
ion
egl
_surface
21
c2100000
00000000
8257536
175
--
L
--
ion
egl
_surface
21
|
其中ion类型(由ION内存分配器分配的内存)的内存块统计到Graphics类别里面,从上图我们可以看到有三块egl_surface,它们对应应用所使用的窗口的三个Buffer,还有一个egl_image暂时不清楚用途,这些都是应用启动后Android自动分配的。gpumem类型的内存块统计到GL类别里面,包括GL里面的纹理(texture),各种shader,vertex buffer等等。另外,因为有些内存块映射到了userspace,有些则没有映射,所以映射到userspace的内存块会被标记为accounted,避免meminfo重复计数,meminfo最终显示的Graphics和GL的内存值是哪些没有映射到userspace的内存块的大小之和。