Android Memory Tracker原理分析

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数据加起来正好等于总和。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345