Android 4.4 meminfo 實現分析

來源:互聯網
上載者:User

Android提供了一個名為meminfo的小工具協助應用分析自身的記憶體佔用,並且在4.4還新增了memtrack HAL模組,SoC廠商通過實現memtrack模組,讓meminfo可以擷取GPU相關的一些記憶體配置狀況。瞭解meminfo的實現,對我們更深入瞭解應用的記憶體佔用狀況是很有協助的。而這篇文章的目的就是分析Android 4.4 meminfo的內部實現源碼,讓開發人員通過這些資訊可以更瞭解自己應用的記憶體佔用狀況。

在控制台輸入命令"adb shell dumpsys meminfo YOUR-PACKAGE-NAME",可以看到類似的結果:

** MEMINFO in pid 14120 [com.UCMobile.test] **                   Pss  Private  Private  Swapped     Heap     Heap     Heap                 Total    Dirty    Clean    Dirty     Size    Alloc     Free                ------   ------   ------   ------   ------   ------   ------  Native Heap   187886   187872        0        0   325232   174093    38594  Dalvik Heap    24801    24444        0        0    41476    35899     5577 Dalvik Other      700      700        0        0                                   Stack      508      508        0        0                               Other dev    33564    32600        4        0                                .so mmap     9019     1244     7268        0                               .apk mmap      101        0       16        0                               .ttf mmap     1330        0      696        0                               .dex mmap     2248        0     2248        0                               code mmap      985        0      188        0                              image mmap     1182      908       12        0                              Other mmap      130        4      108        0                                Graphics    25504    25504        0        0                                      GL     2196     2196        0        0                                 Unknown    32476    32476        0        0                                   TOTAL   322630   308456    10540        0   366708   209992    44171

實際的調用代碼入口在android.os.Debug.java和對應的CPP檔案android_os_Debug.cpp,Debug.java的getMeminfo方法實際上調用了android_os_Debug.cpp的android_os_Debug_getDirtyPagesPid方法。

static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,        jint pid, jobject object){    stats_t stats[_NUM_HEAP];    memset(&stats, 0, sizeof(stats));    load_maps(pid, stats);    struct graphics_memory_pss graphics_mem;    if (read_memtrack_memory(pid, &graphics_mem) == 0) {        ...    }    ...}static void load_maps(int pid, stats_t* stats){    char tmp[128];    FILE *fp;    sprintf(tmp, "/proc/%d/smaps", pid);    fp = fopen(tmp, "r");    if (fp == 0) return;    read_mapinfo(fp, stats);    fclose(fp);}

從上面的代碼可以看到,android_os_Debug_getDirtyPagesPid方法先調用了load_maps方法,而load_maps方法要做的事情也很簡單,它開啟/proc/PID/smaps虛擬檔案,讀取裡面的資訊,在已ROOT的裝置上,我們可以通過“adb shell cat /proce/PID/smaps”直接將這個虛擬檔案的資訊列印在控制台上。

80ff5000-810f2000 rw-p 00000000 00:00 0          [stack:12211]Size:               1012 kBRss:                   4 kBPss:                   4 kB...81100000-811a4000 rw-s 000f4000 00:0b 6285       /dev/kgsl-3d0Size:                656 kBRss:                 652 kBPss:                 352 kB...811d1000-811e0000 rw-p 00000000 00:00 0          [anon:libc_malloc]Size:                 60 kBRss:                  60 kBPss:                  60 kB...Name:           [anon:libc_malloc]

“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這兩項的記憶體使用量資訊。

/* * Uses libmemtrack to retrieve graphics memory that the process is using. * Any graphics memory reported in /proc/pid/smaps is not included here. */static int read_memtrack_memory(struct memtrack_proc* p, int pid,        struct graphics_memory_pss* graphics_mem){    int err = memtrack_proc_get(p, pid);    ...    ssize_t pss = memtrack_proc_graphics_pss(p);    ...    graphics_mem->graphics = pss / 1024;    pss = memtrack_proc_gl_pss(p);    ...    graphics_mem->gl = pss / 1024;    pss = memtrack_proc_other_pss(p);    ...    graphics_mem->other = pss / 1024;    return 0;}

read_memtrack_memory方法的實現如所示,它讀取了Graphics,GL,Other這三類記憶體資訊,而這三個類別的定義在hardware/memtrack.h裡面。

/* * The Memory Tracker HAL is designed to return information about device-specific * memory usage.  The primary goal is to be able to track memory that is not * trackable in any other way, for example texture memory that is allocated by * a process, but not mapped in to that process' address space. * A secondary goal is to be able to categorize memory used by a process into * GL, graphics, etc.  All memory sizes should be in real memory usage, * accounting for stride, bit depth, rounding up to page size, etc. * * A process collecting memory statistics will call getMemory for each * combination of pid and memory type.  For each memory type that it recognizes * the HAL should fill out an array of memtrack_record structures breaking * down the statistics of that memory type as much as possible.  For example, * getMemory(, MEMTRACK_TYPE_GL) might return: * { { 4096,  ACCOUNTED | PRIVATE | SYSTEM }, *   { 40960, UNACCOUNTED | PRIVATE | SYSTEM }, *   { 8192,  ACCOUNTED | PRIVATE | DEDICATED }, *   { 8192,  UNACCOUNTED | PRIVATE | DEDICATED } } * If the HAL could not differentiate between SYSTEM and DEDICATED memory, it * could return: * { { 12288,  ACCOUNTED | PRIVATE }, *   { 49152,  UNACCOUNTED | PRIVATE } } * * Memory should not overlap between types.  For example, a graphics buffer * that has been mapped into the GPU as a surface should show up when * MEMTRACK_TYPE_GRAPHICS is requested, and not when MEMTRACK_TYPE_GL * is requested. */enum memtrack_type {    MEMTRACK_TYPE_OTHER = 0,    MEMTRACK_TYPE_GL = 1,    MEMTRACK_TYPE_GRAPHICS = 2,    MEMTRACK_TYPE_MULTIMEDIA = 3,    MEMTRACK_TYPE_CAMERA = 4,    MEMTRACK_NUM_TYPES,};

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裡面。

int kgsl_memtrack_get_memory(pid_t pid, enum memtrack_type type,                             struct memtrack_record *records,                             size_t *num_records){    ...    sprintf(tmp, "/d/kgsl/proc/%d/mem", pid);    fp = fopen(tmp, "r");    ...    if (type == MEMTRACK_TYPE_GL) {        sprintf(tmp, "/proc/%d/smaps", pid);        smaps_fp = fopen(tmp, "r");        ...    }    while (1) {        unsigned long uaddr;        unsigned long size;        char line_type[7];        int ret;        if (fgets(line, sizeof(line), fp) == NULL) {            break;        }        /* Format:         *  gpuaddr useraddr     size    id flags       type            usage sglen         * 545ba000 545ba000     4096     1 ----p     gpumem      arraybuffer     1         */        ret = sscanf(line, "%*x %lx %lu %*d %*s %6s %*s %*d\n",                     &uaddr, &size, line_type);        if (ret != 3) {            continue;        }        if (type == MEMTRACK_TYPE_GL && strcmp(line_type, "gpumem") == 0) {            bool accounted = false;            /*             * We need to cross reference the user address against smaps,             *  luckily both are sorted.             */            while (smaps_addr <= uaddr) {                unsigned long start;                unsigned long end;                unsigned long smaps_size;                if (fgets(line, sizeof(line), smaps_fp) == NULL) {                    break;                }                if (sscanf(line, "%8lx-%8lx", &start, &end) == 2) {                    smaps_addr = start;                    continue;                }                if (smaps_addr != uaddr) {                    continue;                }                if (sscanf(line, "Rss: %lu kB", &smaps_size) == 1) {                    if (smaps_size) {                        accounted = true;                        accounted_size += size;                        break;                    }                }            }            if (!accounted) {                unaccounted_size += size;            }        } else if (type == MEMTRACK_TYPE_GRAPHICS && strcmp(line_type, "ion") == 0) {            unaccounted_size += size;        }    }    ...}

kgsl_memtrack_get_memory是memtrack的getMemory方法的具體實現,我們可以看到它實際上是讀取一張內部的GPU記憶體配置表的資訊(虛擬檔案/d/kgsl/proc/PID/mem),在已ROOT的裝置上,我們可以通過“adb shell cat /d/kgsl/proc/PID/mem”將這張記憶體配置表的資訊列印到控制台上,如所示:

 gpuaddr useraddr     size    id flags       type            usage sglen7565e000 00000000     4096     1 ----p     gpumem      arraybuffer     1756bc000 00000000    65536     2 -r--p     gpumem          command    16756cd000 00000000    65536     3 -r--p     gpumem          command    16756de000 00000000    65536     4 -r--p     gpumem          command    16756fb000 00000000     4096     5 ----p     gpumem               gl     175fe2000 00000000   262144     6 ----p     gpumem               gl    6476023000 00000000     8192     7 ----p     gpumem               gl     276026000 00000000     8192     8 ----p     gpumem               gl     276029000 00000000     4096     9 ----p     gpumem          texture     1...94d71000 00000000   131072   362 ----p     gpumem  vertexarraybuff    3294da0000 00000000   667648   176 --l-p     gpumem          texture   16394e44000 00000000   131072   363 ----p     gpumem           any(0)    3294e65000 00000000   131072   364 ----p     gpumem           any(0)    32c0000000 00000000 17268736    31 --L--        ion        egl_image  4216c1100000 00000000  8257536    36 --L--        ion      egl_surface    21c1900000 00000000  8257536   164 --L--        ion      egl_surface    21c2100000 00000000  8257536   175 --L--        ion      egl_surface    21

其中ion類型(由ION記憶體 Clerk分配的記憶體)的記憶體塊統計到Graphics類別裡面,從我們可以看到有三塊egl_surface,它們對應應用所使用的視窗的三個Buffer,還有一個egl_image暫時不清楚用途(這塊17M的egl_image,在不同進程裡面的地址和ID號都是一樣的,所以猜測實際是Android在不同應用之間分享的Assert Atlas,Android 4.4開始,把系統的圖片資源拼接成一大塊紋理,然後通過GraphicBuffer + EGLImage在不同的應用之間共用),這些都是應用啟動後Android自動分配的。gpumem類型的記憶體塊統計到GL類別裡面,包括GL裡面的紋理(texture),各種shader,vertex buffer等等。另外,因為有些記憶體區塊對應到了userspace,有些則沒有映射,所以映射到userspace的記憶體塊會被標記為accounted,避免meminfo重複計數,meminfo最終顯示的Graphics和GL的記憶體值是哪些沒有映射到userspace的記憶體塊的大小之和。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.