`

Android 4.4 meminfo 实现分析

 
阅读更多

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容量大小

    android的平板本身ddr只有1G,客户要求改成显示2G,就是antutu上要显示2G,在kernel的上做了些处理,在4.4上能正常显示和使用,新版本没验证过

    内存监视软件(MemInfo)

    一个绿色小巧的内存监视软件,基本上汉化了,但也有一些英文,但相信一定难不倒精明的你!

    Android Vmstat详解及问题快速定位

    其实现原理是在一定时间内(可通过-d命令设置,默认是1秒),读取并分析系统/proc文件系统下的 meminfo、stat和vmstat信息并做归总整理、统计,以直观明了的信息以供用户或开发人员检测系统状态。

    Android获取系统储存以及内存信息的方法(二)

    Android获取储存信息以及内存信息可以用adb命令查看。 adb查看系统内存信息以及储存信息: 命令:adb shell cat /proc/meminfo 代码: private static final String FILENAME_PROC_MEMINFO = /proc/meminfo; /*...

    php-meminfo:PHP扩展可深入了解内存使用情况

    MEMINFO PHP Meminfo是一个PHP扩展,可让您深入了解PHP内存内容。 其主要目的是帮助您了解内存泄漏:通过查看内存中存在的数据,您可以更好地了解您的应用程序行为。 该工具的主要灵感来源之一是带有-histo选项的...

    MemInfo v3.42.zip

    MemInfo可以在计算机右下方的系统列,显示出实体记忆的使用状态,以及虚拟内存的使用状态。让你可以时时监控着你计算机内存的使用状态。且它还提供了一个整理内存的功能,以免计算机用久了,可用内存愈来愈少,而...

    Android编程实现悬浮窗获取并显示当前内存使用量的方法

    本文实例讲述了Android编程实现悬浮窗获取并显示当前内存使用量的方法。分享给大家供大家参考,具体如下: 运行效果: 其中: 这一块就是悬浮窗,可以随意拖动,动态显示当前内存使用量。 下面看一下代码是如何...

    Node.js-Android性能测试工具Emmagee

    Emmagee是监控指定被测应用在使用过程中占用机器的CPU、内存、流量资源的性能测试小工具。

    hazelnut:用于解析procmeminfo的pythonic库

    hazelnut是APACHE许可的库,它使用Python编写,旨在提供一种简单且Pythonic的方式来解析基于LINUX的系统上的/ proc / meminfo文件。 该库已经过Python 2.7.x和Python 3.6.x的测试。 安装: 从源使用 $ python ...

    android_server

    版本 0.0.0.1 这是尝试从 idasdk66 重新编译 android_server。 此时它以某种方式编译(并从... bool linux_debmod_t::add_android_shlib_bpt(const meminfo_vec_t &miv, bool attaching) { return false; } #万一

    Android编程实现获得内存剩余大小与总大小的方法

    本文实例讲述了Android编程实现获得内存剩余大小与总大小的方法。分享给大家供大家参考,具体如下: public class memInfo { // 获得可用的内存 public static long getmem_UNUSED(Context mContext) { long MEM...

    Android初始化流程

    本文从代码的角度描述了Android初始化流程,并附带了:Android启动后的进程列表;用eclipse保存下来的启动log;一个真机的meminfo.其中,关于代码流程,运用并不正规的UML语言来描述.

    Android 如何获取手机总内存和可用内存等信息

    在android开发中,有时候我们想获取手机... “/proc/meminfo”文件记录了android手机的一些内存信息,在命令行窗口里输入”adb shell“,进入shell环境,输入”cat /proc/meminfo”即可在命令行里显示meminfo文件的内

    meminfo.py

    首先通过adb连接上Android的设备,然后运行附件中的脚本,输入需要被获取内存大小的应用包名,即可得到它的实时占用内存大小。

    android系统信息(内存、cpu、sd卡、电量、版本)获取.txt

    android系统信息(内存、cpu、sd卡、电量、版本)获取.txt android的总内存大小信息存放在系统的/proc/meminfo文件里面,可以通过读取这个文件来获取这些信息:

    Android获取设备CPU核数、时钟频率以及内存大小的方法

    本文实例讲述了Android获取设备CPU核数、时钟频率以及内存大小的方法。分享给大家供大家参考,具体如下: 因项目需要,分析了一下 Facebook 的开源项目 – Device Year Class。 Device Year Class 的主要功能是根据 ...

    Android系统检测程序内存占用各种方法

    liuhx@uc ~ $ adb shell cat /proc/meminfo MemTotal: 840868 kB MemFree: 457344 kB Buffers: 1744 kB Cached: 203064 kB SwapCached: 0 kB Active: 234932 kB Inactive: 129644 kB Active(anon): 170292 ...

    meminfo:显示有关进程的内存信息-开源

    这个小的头文件是纯C的,可以在任何C / C ++程序中使用以获取有关当前进程的内存信息。 您是否曾经尝试在任务管理器或类似程序中监视进程,并想知道为什么即使程序释放了所有内存,内存也不会减少?...

    Delphi获取Windows物理及虚拟内存信息.rar

     MemInfo.dwLength:=sizeof(MEMORYSTATUS);  //用sizeof(MEMORYSTATUS)填充dwLength成员  GlobalMemoryStatus(MemInfo);  //获取内存信息  Edit1.Text:=IntToStr(MemInfo.dwMemoryLoad) '%';  //内存使用...

    LogicLog Adb.exe

    [Unity3D] android adb 命令 工具目录不要有中文 简介 adb的全称为Android Debug Bridge,就是起到调试桥的作用。通过adb我们可以在Eclipse中方面通过DDMS来调试Android程序,说白了就是debug工具。adb的工作方式...

Global site tag (gtag.js) - Google Analytics