Android provides a small tool named meminfo to help the application analyze its memory usage. In addition, the memtrack HAL module is added in 4.4. The SoC vendor implements the memtrack module, let meminfo Get Some GPU-related memory allocation conditions. Understanding the implementation of meminfo is helpful for us to learn more about the memory usage of the application. The purpose of this article is to analyze the internal implementation source code of Android 4.4 meminfo, so that developers can better understand the memory usage of their applications.
Enter the command "adb shell dumpsys meminfo YOUR-PACKAGE-NAME" on the console, and you can see similar results:
** 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
The actual call code entry is in android. OS. Debug. java and the corresponding CPP file android_ OS _Debug.cpp. The getMeminfo method of Debug. java actually calls android_ OS _Debug.cpp's android_ OS _Debug_getDirtyPagesPid method.
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);}
From the code above, we can see that the android_ OS _Debug_getDirtyPagesPid method first calls the load_maps method, and the load_maps method is easy to do. It opens the/proc/PID/smaps Virtual File and reads the information in it, on a ROOT device, we can directly print the information of this virtual file on the console through "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]
Shows the information output by "adb shell cat/proce/PID/smaps". It is actually the memory allocation table of the application's userspace address space, recording the address of each memory allocated by the application, class, size, and other information. The load_maps method calls the read_mapinfo method to read the allocation information of each piece of memory from this table, and accumulate the classes to obtain the Native Heap, memory usage of Dalvik Heap and other categories.
However, some memory blocks in all the memory used by the application are not mapped to the userspace address space of the process (mainly the memory used by the GPU ), the memory block information cannot be found in smaps. Therefore, a memtrack HAL module is added in Android 4.4 to be implemented by the SoC vendor. If the SoC vendor implements the memtrack module, meminfo can call libmemtrack to obtain GPU-related memory usage information. Therefore, we can see that the android_ OS _Debug_getDirtyPagesPid method reads the memory usage information of Graphics and GL by calling the read_memtrack_memory method.
/* * 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;}
Shows the implementation of the read_memtrack_memory method. It reads information about Graphics, GL, and Other memory, and defines the three categories in 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 corresponds to MEMTRACK_TYPE_GRAPHICS, GL corresponds to MEMTRACK_TYPE_GL, and Other is actually the sum of MEMTRACK_TYPE_OTHER, MEMTRACK_TYPE_MULTIMEDIA, MEMTRACK_TYPE_CAMERA. Memtrack is implemented by SoC vendors. In the AOSP source code, we can find the Qualcomm implementation source code in 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 is the specific implementation of the getMemory method of memtrack. We can see that it actually reads the information of an internal GPU Memory Allocation Table (Virtual File/d/kgsl/proc/PID/mem ), on the ROOT device, we can print the information of this memory allocation table to the console through "adb shell cat/d/kgsl/proc/PID/mem", as shown in:
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
The memory blocks of the ion type (memory allocated by the ION memory distributor) are counted into the Graphics category. We can see three egl_surface blocks, they correspond to the three buffers in the window used by the application, and an egl_image is not clear for the time being (the address and ID number of this 17M egl_image are the same in different processes, so I guess it's actually the Assert Atlas that Android shares between different apps. Android 4.4 starts to splice the image resources of the system into a large texture, then use GraphicBuffer + EGLImage to share among different applications. These are automatically allocated by Android after the application is started. Memory blocks of the gpumem type are counted into the GL category, including texture, various shader, and vertex buffer in the GL. In addition, because some memory blocks are mapped to userspace, some do not have mappings, the memory blocks mapped to userspace are marked as accounted to avoid repeated meminfo counts, the memory values of Graphics and GL displayed by meminfo are the sum of memory blocks not mapped to userspace.