1. 背景
公司有一个测试项,执行adb shell dumpsys meminfo + pkg
命令查看内存信息:
malk@malk:am$ adb shell dumpsys meminfo com.meizu.safe
Applications Memory Usage (in Kilobytes):
Uptime: 103881237 Realtime: 263009590
** MEMINFO in pid 32506 [com.meizu.safe] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 18894 18832 0 0 32768 20940 11827
Dalvik Heap 6713 6624 0 0 10615 6519 4096
Dalvik Other 1485 1484 0 0
Stack 700 700 0 0
Ashmem 2 0 0 0
Other dev 4 0 4 0
.so mmap 2717 136 40 39
.apk mmap 44952 0 35456 0
.ttf mmap 45 0 0 0
.dex mmap 5344 8 1324 0
.oat mmap 2728 0 0 0
.art mmap 2871 2368 4 2
Other mmap 1090 4 64 0
EGL mtrack 12168 12168 0 0
Unknown 1535 1532 0 0
TOTAL 101289 43856 36892 41 43383 27459 15923
App Summary
Pss(KB)
------
Java Heap: 8996
Native Heap: 18832
Code: 36964
Stack: 700
Graphics: 12168
Private Other: 3088
System: 20541
TOTAL: 101289 TOTAL SWAP PSS: 41
一直有个疑问,dump信息中EGL mtrack/GL mtrack两项分别代表什么意思?
google官方解释:
EGL memtrack
You will see this column when display driver’s memtrack module is enabled
Before Lollipop5.1, this column is named “Graphics”.
EGL memtrack memory is the summary of all surface buffers(the surface buffer increases to triple buffer after Android 4.1) and the size of the Atlas buffer.
However, Atlas buffer is actually a shared memory and shouldn’t be accounted into each UI process’ memory usage to overcount the memory usage.
Both surface buffer and Atlas buffer’s memory quota is reserved in project’s memory estimation, thus the memory usage of these buffers should be separately
accounted from process’ memory usage. So when you measure process’ memory usage, you can ignore this column.
GL memtrack
You will see this column when display driver’s memtrack module is enabled
Before Lollipop5.1, this column is named “GL”.
HW acceleration memory is partially counted in process PSS. For example, for QCT platform the HW acceleration memory is partially counted in the PSS of /dev/kgsl-3d0
as we mentioned in the “Gfx dev” section. GL memtrack memory usage calculates the unaccounted /dev/kgsl-3d0 memory regions which PSS value equals 0.
Please be noticed that the summation of GL memtrack and Gfx dev doesn’t reflect the complete HW acceleration memory since the full HW acceleration memory usage
should be counted with the VSS of /dev/kgsl-3d0. So the “TOTAL” value of dumpsys meminfo is smaller than actual physical memory usage.
只看简介有些难以理解,我想了解它的生成原理,找了半天,发现qualcomm和mtk平台相关代码没有开源,还好samsung开源了代码。
先说明一下这两项的含义:
name | description |
---|---|
EGL mtrack | gralloc分配的内存,主要是窗口系统,SurfaceView/TextureView和其他的由gralloc分配的GraphicBuffer总和 |
GL mtrack | 驱动上报的GL内存使用情况。 主要是GL texture大小,GL command buffer,固定的全局驱动程序RAM开销等的总和 |
2. Memory Tracker
2.1 Memory Tracker是什么
Memory Tracker也称memtrack,是一个hal层的库,不同平台库的名称不同,实现方式也有差异。
以samsung平台为例:
Name: memtrack.exynos5.so
File Directory:/system/lib64/hw/
Source code : hardware/samsung_slsi/exynos/libmemtrack/
Memory Tracker 主要目标是能够跟踪以任何其他方式无法跟踪的内存,例如由进程分配但未映射到进程地址空间的纹理内存。
第二个目标是能够将进程使用的内存分类为GL,graphics等。所有的内存大小应该在实际的内存使用情况下,考虑到stride,bit depth,page size等。
EGL mtrack/GL mtrack这两项的数据就是通过memtrack获取的。
2.2 adb shell dumpsys meminfo实现原理
先看一下adb shell dumpsys meminfo +pkg
的流程
1. Ams在systemserver启动时,创建了meminfo服务,所以才能dumpsys meminfo:
ServiceManager.addService("meminfo", new MemBinder(this));
2.执行dumpsys操作后,会去获取两部分信息:
static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
load_maps(pid, stats, &foundSwapPss);//获取/proc/$pid/smap节点信息
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {//获取graphics drivers上报的内存信息
stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
stats[HEAP_GL].pss = graphics_mem.gl;
stats[HEAP_GL].privateDirty = graphics_mem.gl;
stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
}
}
static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
char tmp[128];
FILE *fp;
sprintf(tmp, "/proc/%d/smaps", pid);
fp = fopen(tmp, "r");
if (fp == 0) return;
read_mapinfo(fp, stats, foundSwapPss);
fclose(fp);
}
/*
* Retrieves the graphics memory that is unaccounted for in /proc/pid/smaps.
* 获取不计算在/proc/pid/smaps里的graphics memory信息
*/
static int read_memtrack_memory(int pid, struct graphics_memory_pss* graphics_mem)
{
if (!memtrackLoaded) {
return -1;
}
struct memtrack_proc* p = memtrack_proc_new();
if (p == NULL) {
ALOGW("failed to create memtrack_proc");
return -1;
}
int err = read_memtrack_memory(p, pid, graphics_mem);
memtrack_proc_destroy(p);
return err;
}
3. graphics memory数据类型
graphics memory分为五种类型数据:
- MEMTRACK_TYPE_OTHER = 0
- MEMTRACK_TYPE_GL = 1
- MEMTRACK_TYPE_GRAPHICS = 2
- MEMTRACK_TYPE_MULTIMEDIA = 3
- MEMTRACK_TYPE_CAMERA = 4
我看到libmemtrack中有判断,只接收两种数据,其他数据对应的代码没有找到:
memtrack_exynos.c
if (type == MEMTRACK_TYPE_GL) {
return mali_memtrack_get_memory(pid, type, records, num_records);
} else if (type == MEMTRACK_TYPE_GRAPHICS) {
return ion_memtrack_get_memory(pid, type, records, num_records);
}
MEMTRACK_TYPE_GL = GL mtrack,MEMTRACK_TYPE_GRAPHICS = EGL mstrack
2.3 Memory Tracker实现原理
graphics memory的数据是由graphics driver统计的,统计方式会因平台不同而有所差异,即使相同应用,不同平台读取的数据也可能不相同。
Memory Tracker做的事情就是将graphics driver统计好的数据从节点读取并格式化。
以samsung平台为例,memtrack.exynos5.so库做的事情很简单,根据上层传递的type读取对应节点,获取内存信息。
memtrack_exynos.c
if (type == MEMTRACK_TYPE_GL) {
return mali_memtrack_get_memory(pid, type, records, num_records);
} else if (type == MEMTRACK_TYPE_GRAPHICS) {
return ion_memtrack_get_memory(pid, type, records, num_records);
}
2.3.1 MEMTRACK_TYPE_GL
如果上层传递的type类型为MEMTRACK_TYPE_GL,对应GL mtrack,说明需要获取的是驱动程序报告的GL内存使用。它主要是GL纹理大小,GL命令缓冲区,固定的全局驱动程序RAM开销等的总和。这些数据储存在/d/mali/mem/
目录下的节点中(samsung),由mali库(或者其他平台的渲染库)进行统计。
以samsung平台,com.meizu.safe应用为例,我们查看其数据:
1|m17:/ # ps | grep safe
system 32459 1174 1849812 127628 SyS_epoll_ 7b483472a0 S com.meizu.safe:MzSecService
system 32506 1174 2904076 225528 SyS_epoll_ 7b483472a0 S com.meizu.safe
m17:/ #
m17:/ #
m17:/ # cat /d/mali/mem/32506_76/mem_profile
com.meizu.safe:
Channel: Unnamed (Total memory: 2384536)
7: 226 / 14464
8: 297 / 47520
9: 16 / 6272
13: 166 / 684696
14: 68 / 601960
15: 4 / 79352
16: 3 / 98304
17: 5 / 327680
18: 4 / 524288
Channel: Default Heap (Total memory: 1678048)
13: 14 / 60744
14: 2 / 27352
16: 6 / 196672
18: 4 / 749088
20: 1 / 644192
Channel: Framepool (Total memory: 0)
(empty)
Channel: Frame Internal (Total memory: 32768)
16: 1 / 32768
Channel: GPU Program (Total memory: 40960)
16: 1 / 40960
Channel: EGL Color Plane (Total memory: 4096)
13: 1 / 4096
Channel: GLES VAO (Total memory: 0)
(empty)
Channel: Image Descriptor (Total memory: 352)
5: 16 / 256
7: 1 / 96
Channel: Texture (Total memory: 6252064)
6: 1 / 32
10: 1 / 512
11: 1 / 1024
12: 1 / 2048
14: 2 / 18816
16: 1 / 51008
17: 7 / 716608
19: 1 / 266240
21: 1 / 1048576
22: 1 / 4147200
Channel: Buffer (Total memory: 1204288)
7: 1 / 64
15: 1 / 24576
18: 1 / 131072
21: 1 / 1048576
Channel: CRC Buffer (Total memory: 102400)
13: 1 / 4096
16: 3 / 98304
Total allocated memory: 11699512
分类说明:
name | description |
---|---|
Default Heap | gpu内部结构的内存分配。是gpu中最基础的内存分配 |
Framepool | framepool是gpu内部的概念,这个channel统计为framepool结构分配的内存。很多函数,例如eglSwapBuffers可能会引起这个channel的内存增加 |
Frame Internal | tiler是gpu内部的概念,这个channel统计为tiler结构分配的内存。很多函数,例如glCreateXXXSurface可能会引起这个channel的内存增加 |
GPU Program | glLinkProgram被调用时,需要分配相应内存来存放shader code,那么这段内存将被记录进GPU Program |
Image Descriptor | image instance是gpu内部的概念,这个channel统计为image instance结构分配的内存。很多函数,例如glDrawXXX可能会引起这个channel的内存增加 |
EGL Color Plane | 为color planes分配的内部buffer。一些函数,例如gpu的回调函数可能会引起这个channel的内存增加 |
GLES VAO | VAO是一个保存了所有顶点数据属性的状态结合,它存储了顶点数据的格式以及顶点数据所需的VBO对象的引用。 |
Texture | 纹理 |
Buffer | glBufferData被调用时,需要分配内存来存放要导入的data,这段内存将被记录进Buffer |
CRC Buffer | 一个color buffer的CRC buffer。一些函数,例如gpu的回调函数可能会引起这个channel的内存增加 |
2.3.1 MEMTRACK_TYPE_GRAPHICS
MEMTRACK_TYPE_GRAPHICS对应EGL mtrack,获取的是gralloc内存使用情况。这些数据储存在/sys/kernel/debug/ion/clients/
目录的节点下(samsung)。
首先说明,graphic memory由进程触发分配,但是不会映射到进程的地址空间。
但是计算时,这部分内存可能会计算到应用的内存中。
以samsung平台为例:
对于没有TextureView和SurfaceView的应用,这个数值的大小应该是系统默认配置的Buffer数量*单个buffer的内存大小
.目前系统默认BufferQueue中Buffer数量是3,那么EGL mtrack = 3 * 单个buffer的内存大小
。
还是以com.meizu.safe应用为例,当应用在前台时,我们查看其数据:
m17:/ # cat /sys/kernel/debug/ion/clients/32506-0
buffer task pid thread tid size # procs flag
------------------------------------------------------------------------------------------
ffffffc00e70d880 surfaceflinger 1069 Binder:1069_4 1241 4153344 1 40
ffffffc02e16b800 surfaceflinger 1069 Binder:1069_4 1241 4153344 1 40
ffffffc06baa3100 surfaceflinger 1069 Binder:1069_2 1075 4153344 1 40
------------------------------------------------------------------------------------------
heap_name: size_in_bytes size_in_bytes(pss)
ion_noncontig_he: 12460032 12460032
其中ion_noncontig_he
代表这个应用由gralloc分配的内存总和。
这里有一个要注意的点,按home键让应用退到后台时,ion_noncontig_he的值就为0。这是一个优化。
看完单个应用,我们再看一下机器整体的数据:
清空多任务,解锁进入桌面:
(1)adb shell dumpsys meminfo
查看总共的EGL mtrack数值。
total = 18380K
(2)adb shell dumpsys meminfo surfaceflinger
查看SurfaceFlinger的值
cat对应节点:
SurfaceFlinger没有EGL mtrack这项统计
虽然app对应的BufferQueue的GraphicBuffer都是由SurfaceFlinger通过gralloc申请的,但是在samsung平台上,EGL mtrack这部分数据并不算在SurfaceFlinger进程。
(3)分别查看system_server、launcher、systemui、recent的数据
| PkgName | EGL mtrack |
| ------------- |:-------------:| -----:|
| system_server | 4224 |
| com.android.systemui | 1988 |
| com.meizu.flyme.launcher | 12168 |
| com.android.systemui:recents | 0 |
| total | 18380 |
各个应用EGL mtrack数据加起来正好等于总和。