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]**PssPrivatePrivateSwappedHeapHeapHeapTotalDirtyCleanDirtySizeAllocFree------------------------------------------NativeHeap1878861878720032523217409338594DalvikHeap24801244440041476358995577DalvikOther70070000Stack50850800Other dev 335643260040.so mmap 9019124472680.apk mmap 1010160.ttf mmap 133006960.dex mmap 2248022480 code mmap 98501880 image mmap 1182908120Other mmap 13041080Graphics255042550400 GL 2196219600Unknown324763247600 TOTAL 32263030845610540036670820999244171
实际的调用代码入口在android.os.Debug.java和对应的CPP文件 android_os_Debug.cpp,Debug.java的getMeminfo方法实际上调用了android_os_Debug.cpp的 android_os_Debug_getDirtyPagesPid方法。
staticvoid 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){...}...}staticvoid 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 0000000000:000[stack:12211]Size:1012 kB Rss:4 kB Pss:4 kB ...81100000-811a4000 rw-s 000f400000:0b6285/dev/kgsl-3d0Size:656 kB Rss:652 kB Pss:352 kB ...811d1000-811e0000 rw-p 0000000000:000[anon:libc_malloc]Size:60 kB Rss:60 kB Pss: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. */staticint 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;return0;}
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){unsignedlong uaddr;unsignedlong 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){unsignedlong start;unsignedlongend;unsignedlong 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;}}elseif(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 sglen 7565e0000000000040961----p gpumem arraybuffer 1756bc00000000000655362-r--p gpumem command 16756cd00000000000655363-r--p gpumem command 16756de00000000000655364-r--p gpumem command 16756fb0000000000040965----p gpumem gl 175fe2000000000002621446----p gpumem gl 64760230000000000081927----p gpumem gl 2760260000000000081928----p gpumem gl 2760290000000000040969----p gpumem texture 1...94d7100000000000131072362----p gpumem vertexarraybuff 3294da000000000000667648176--l-p gpumem texture 16394e4400000000000131072363----p gpumem any(0)3294e6500000000000131072364----p gpumem any(0)32 c0000000 000000001726873631--L-- ion egl_image 4216 c1100000 00000000825753636--L-- ion egl_surface 21 c1900000 000000008257536164--L-- ion egl_surface 21 c2100000 000000008257536175--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的内存块 的大小之和。
相关推荐
因此,修改"android4.4修改上报系统显示ddr容量大小"的问题,需要深入到内核层面,对`meminfo.c`进行调整。 `meminfo.c`是Linux内核中负责生成`/proc/meminfo`文件内容的部分。在这个文件中,通常有函数用于计算和...
### Android Bugreport 分析:掌握核心技能 在深入解析Android bugreport之前,我们先了解其基本概念。Android bugreport是一个全面的系统日志文件,它包含了大量的系统信息,旨在帮助开发者诊断和解决Android设备...
`dumpsys` 的实现主要在`mydroid/frameworks/base/cmds/dumpsys/dumpsys.cpp`文件中,它通过重载`Binder`类的`dump`方法来收集各种服务的信息。每个服务的`dump`信息由相应服务的`Binder`对象提供,这些对象在系统...
"android分析工具说明书.rar"这个压缩包很可能是提供了一份详尽的指南,介绍了如何使用各种Android分析工具来分析程序内存、监控对象属性变化等关键任务。 一、Android分析工具介绍 1. **Android Studio Profiler*...
这涉及到系统层面的信息查询,通常需要通过编程接口(API)或者使用Android Debug Bridge(ADB)工具来实现。以下是对这个主题的详细阐述: ### 1. Android API 获取内存信息 Android提供了一个名为`...
在Android平台上,获取CPU、内存和磁盘使用率信息对于开发者来说是十分重要的,这...通过分析这个示例代码,你可以进一步理解如何在实践中实现上述功能。学习并理解这个示例将对掌握Android系统信息获取有极大的帮助。
获取 android 手机内存信息可以通过读取 `/proc/meminfo` 文件和使用 `ActivityManager.getMemoryInfo(ActivityManager.MemoryInfo)` 方法来实现。这可以帮助开发者更好地优化应用程序的性能和资源利用率。
在Android系统中,获取硬件信息是开发者经常需要进行的操作,特别是在开发与硬件性能密切相关的应用时。本示例主要关注如何...分析这个文件,可以帮助理解具体的实现方式和代码逻辑,从而更好地应用到自己的项目中。
一个绿色小巧的内存监视软件,基本上汉化了,但也有一些英文,但相信一定难不倒精明的你!
Emmagee是一款基于Node.js开发的高性能测试工具,专为Android应用程序设计,用于实时监控和分析应用程序在运行时对系统资源的消耗情况。通过使用Emmagee,开发者可以深入了解其应用在Android设备上的运行性能,包括...
视频问题汇总主要涉及到基于RK平台的视频播放故障,涵盖了多个型号如RK3026、RK3028A、RK3188等,以及Android 4.2和4.4系统。以下是对这些问题的详细分析: 1. **视频编码库版本确认**:在遇到视频播放问题时,首先...
在Android平台上,获取CPU使用率和内存实时数据是开发者进行性能监控、优化应用或实现系统监控功能的关键步骤。本文将详细介绍如何在Android中获取这些关键信息,并提供相关的代码示例。 首先,我们要理解CPU使用率...
总结一下,获取Android手机内存信息主要通过读取`/proc/meminfo`文件和利用`ActivityManager`的`getMemoryInfo()`方法。理解这些内存信息的含义对于优化Android应用至关重要。在实际开发中,应结合两者以获取最准确...
这些调试命令的实践与分析对于解决Android应用中的各种问题,提升应用性能和用户体验具有重要作用。开发者应熟练掌握这些工具,以便在遇到问题时能够快速定位并修复。通过持续的调试和优化,开发者可以确保应用运行...
Gen1与Gen2是Dalvik虚拟机(在Android 4.4之前)和ART(Android运行时)内存堆的一部分,它们代表了垃圾回收(Garbage Collection, GC)的两个不同阶段。了解这两个阶段的区别以及如何判断设备使用的是哪个阶段,...
Android 获取手机信息 Android 操作系统提供了多种方式来获取手机信息,包括获取安装的应用信息、手机可用内存和总内存、手机 CPU 信息等。在本文中,我们将详细介绍如何使用 Java 代码来获取这些信息。 获取安装...
在名为`GetCellPhoneInfo`的项目或应用中,可能会包含实现这些功能的代码示例,例如解析系统文件获取硬件信息,或者使用Android SDK提供的API进行查询。这样的工具可以帮助快速、方便地查看设备硬件详情,为设备管理...
总结而言,Android的低内存管理策略是一套复杂而高效的机制,它通过精细控制不同类型的进程,实现了在有限资源条件下,优化用户体验和系统稳定性的目标。无论是开发者还是普通用户,了解并合理利用这些机制,都能...
对于Android内存的监控和调试,我们可以使用adb工具进行深入分析。在没有专门的调试工具如trace32时,可以执行以下步骤: 1. 连接adb shell到设备。 2. 创建并挂载一个debugfs文件系统:`mkdir /data/debug`,然后`...
在Android应用开发中,如果需要获取设备的内存状态,可以通过JNI来实现。 首先,我们需要了解JNI的基本概念。JNI提供了一套接口,使得Java代码可以调用本地(Native)代码,也就是非Java语言编写的代码。在Android...