Android MEM性能数据获取

查看内存使用情况
使用adb dumpsys 命令 adb shell dumpsys meminfo 其中,package_name 也可以换成程序的pid,pid可以通过 adb shell top | grep app_name 来查找,下图是滴滴主端的内存使用情况

应用级内存

didi@bogon  ~  adb shell dumpsys meminfo com.sdu.didi.psnger
Applications Memory Usage (in Kilobytes):
Uptime: 180975 Realtime: 180975
** MEMINFO in pid 7914 [com.sdu.didi.psnger] **
                   Pss  Private  Private  SwapPss     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap    32081    31972       92      294    59904    41985    17918
  Dalvik Heap    26859    26764        0      318    27257    16354    10903
 Dalvik Other     6354     6348        0        0
        Stack     1424     1424        0        0
       Ashmem     1558     1544        0        0
      Gfx dev     4942     1636        8        0
    Other dev       27        0       24        0
     .so mmap    13066      384     8904      199
    .apk mmap    19677       80    16676        0
    .ttf mmap       11        0        0        0
    .dex mmap    48628       24    37604        0
    .oat mmap    12470        0     3128        0
    .art mmap     2754     1872       44       15
   Other mmap     2227        8     1256        0
   EGL mtrack    41100    41100        0        0
    GL mtrack     6984     6984        0        0
      Unknown     5939     5924       12       99
        TOTAL   227026   126064    67748      925    87161    58339    28821
 App Summary
                       Pss(KB)
                        ------
           Java Heap:    28680
         Native Heap:    31972
                Code:    66800
               Stack:     1424
            Graphics:    49728
       Private Other:    15208
              System:    33214
               TOTAL:   227026       TOTAL SWAP PSS:      925
 Objects
               Views:      121         ViewRootImpl:        1
         AppContexts:        3           Activities:        1
              Assets:        4        AssetManagers:        3
       Local Binders:       75        Proxy Binders:       35
       Parcel memory:       37         Parcel count:      131
    Death Recipients:        1      OpenSSL Sockets:        7
 SQL
         MEMORY_USED:      945
  PAGECACHE_OVERFLOW:      263          MALLOC_SIZE:       62
 DATABASES
      pgsz     dbsz   Lookaside(b)          cache  Dbname
         4       88            135        16/40/7  /storage/emulated/0/Android/data/com.sdu.didi.psnger/files/im/im_database_282680000258050.db
         4       28             45      171/107/4  /data/user/0/com.sdu.didi.psnger/databases/dns_record.db
         4       32             84        10/24/7  /data/user/0/com.sdu.didi.psnger/databases/ad
         4       20             37        46/22/5  /data/user/0/com.sdu.didi.psnger/databases/location_info.db
         4       24             41         5/19/2  /data/user/0/com.sdu.didi.psnger/databases/download_file.db
         4      100            149       58/44/25  /data/user/0/com.sdu.didi.psnger/databases/DIDI_DATABASE
         4       20             19         0/23/2  /data/user/0/com.sdu.didi.psnger/databases/audio_record_2
         4       12                         0/0/0    (attached) temp
         4       20             56         3/15/3  /data/user/0/com.sdu.didi.psnger/databases/audio_record_2 (1)

重点关注如下几个字段:

(1) 私有(Clean and Dirty)内存

进程独占的内存,也就是应用进程销毁时系统可以直接回收的内存容量。 通常来说,“private dirty”内存是其最重要的部分,因为只被自己的进程使用。它只在内存中存储,因此不能做分页存储到外存(Android不支持swap)。 所有分配的Dalvik堆和本地堆都是“private dirty”内存;Dalvik堆和本地堆中和Zygote进程共享的部分是共享dirty内存。

(2) Total 的 PSS 信息

实际使用内存,这是另一种应用内存使用的计算方式,这个值就是我们应用真正占据的内存大小。 PSS会把跨进程的共享页也计算在内。任何独占的内存页直接计算它的PSS值,而和其它进程共享的页则按照共享的比例计算PSS值。例如,在两个进程间共享的页,计算进每个进程PPS的值是它的一半大小。 PSS计算方式的一个好处是:把所有进程的PSS值加起来就可以确定所有进程总共占用的内存。这意味着用PSS来计算进程的实际内存使用、进程间对比内存使用和总共剩余内存大小是很好的方式。

通常来说,只需关心Pss Total列和Private Dirty列就可以了。在一些情况下,Private Clean列和Heap Alloc列也会提供很有用的信息。

代码示例

/**
     * 获取进程内存Private Dirty数据
     *
     * @param context
     * @param pid
     *            进程ID
     * @return nativePrivateDirty、dalvikPrivateDirty、 TotalPrivateDirty
     */
    public static long[] getPrivDirty(Context context, int pid) {
        ActivityManager mAm = (ActivityManager) context
                .getSystemService(Context.ACTIVITY_SERVICE);
        int[] pids = new int[1];
        pids[0] = pid;
        MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids);
        MemoryInfo pidMemoryInfo = memoryInfoArray[0];
        long[] value = new long[3]; // Natvie Dalvik Total
        value[0] = pidMemoryInfo.nativePrivateDirty;
        value[1] = pidMemoryInfo.dalvikPrivateDirty;
        value[2] = pidMemoryInfo.getTotalPrivateDirty();
        return value;
    }
/**
    * 获取进程内存PSS数据
    *
    * @param context
    * @param pid
    * @return nativePss、dalvikPss、TotalPss
    */
   public static long[] getPSS(Context context, int pid) {
       long[] value = new long[3]; // Natvie Dalvik Total
       if (pid >= 0) {
           int[] pids = new int[1];
           pids[0] = pid;
           ActivityManager mAm = (ActivityManager) context
                   .getSystemService(Context.ACTIVITY_SERVICE);
           MemoryInfo[] memoryInfoArray = mAm.getProcessMemoryInfo(pids);
           MemoryInfo pidMemoryInfo = memoryInfoArray[0];
           value[0] = pidMemoryInfo.nativePss;
           value[1] = pidMemoryInfo.dalvikPss;
           value[2] = pidMemoryInfo.getTotalPss();
       } else {
           value[0] = 0;
           value[1] = 0;
           value[2] = 0;
       }
       return value;
   }

获取手机总内存和可用内存信息

“/proc/meminfo”文件记录了android手机的一些内存信息,通过读取文件”/proc/meminfo”的信息能够获取手机Memory的总量。

# cat /proc/meminfo
cat /proc/meminfo
MemTotal: 94096 kB           所有可用RAM大小。
MemFree: 1684 kB           LowFree与HighFree的总和,被系统留着未使用的内存。
Buffers: 16 kB           用来给文件做缓冲大小
Cached: 27160 kB   被高速缓冲存储器(cache memory)用的内存的大小(等于diskcache minus SwapCache)。
SwapCached: 0 kB           被高速缓冲存储器(cache memory)用的交换空间的大小。已经被交换出来的内存,仍然被存放在swapfile中,用来在需要的时候很快的被替换而不需要再次打开I/O端口。
Active: 35392 kB          在活跃使用中的缓冲或高速缓冲存储器页面文件的大小,除非非常必要,否则不会被移作他用。
Inactive: 44180 kB           在不经常使用中的缓冲或高速缓冲存储器页面文件的大小,可能被用于其他途径。
Active(anon): 26540 kB          
Inactive(anon): 28244 kB          
Active(file): 8852 kB          
Inactive(file): 15936 kB          
Unevictable: 280 kB          
Mlocked: 0 kB          
SwapTotal: 0 kB           交换空间的总大小。
SwapFree: 0 kB           未被使用交换空间的大小。
Dirty: 0 kB          等待被写回到磁盘的内存大小。
Writeback: 0 kB          正在被写回到磁盘的内存大小。
AnonPages: 52688 kB          未映射页的内存大小。
Mapped: 17960 kB          设备和文件等映射的大小。
Slab: 3816 kB          内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗。
SReclaimable: 936 kB          可收回Slab的大小。
SUnreclaim: 2880 kB          不可收回Slab的大小(SUnreclaim+SReclaimable=Slab)。
PageTables: 5260 kB          管理内存分页页面的索引表的大小。
NFS_Unstable: 0 kB          不稳定页表的大小。
Bounce: 0 kB         
WritebackTmp: 0 kB         
CommitLimit: 47048 kB         
Committed_AS: 1483784 kB         
VmallocTotal: 876544 kB         
VmallocUsed: 15456 kB         
VmallocChunk: 829444 kB

要获取android手机总内存大小,只需读取”/proc/meminfo”文件的第1行,并进行简单的字符串处理即可。

代码示例

/**
     * 获取空闲内存和总内存拼接字符串
     *
     * @return 总内存
     */
    public static String getFreeAndTotalMem() {
        long[] memInfo = getMemInfo();
        return Long.toString(memInfo[1] + memInfo[2] + memInfo[3]) + "M/"
                + Long.toString(memInfo[0]) + "M";
    }
/**
     * 获取内存信息:total、free、buffers、cached,单位MB
     *
     * @return 内存信息
     */
    public static long[] getMemInfo() {
        long memInfo[] = new long[4];
        try {
            Class<?> procClazz = Class.forName("android.os.Process");
            Class<?> paramTypes[] = new Class[] { String.class, String[].class,
                    long[].class };
            Method readProclines = procClazz.getMethod("readProcLines",
                    paramTypes);
            Object args[] = new Object[3];
            final String[] memInfoFields = new String[] { "MemTotal:",
                    "MemFree:", "Buffers:", "Cached:" };
            long[] memInfoSizes = new long[memInfoFields.length];
            memInfoSizes[0] = 30;
            memInfoSizes[1] = -30;
            args[0] = new String("/proc/meminfo");
            args[1] = memInfoFields;
            args[2] = memInfoSizes;
            if (null != readProclines) {
                readProclines.invoke(null, args);
                for (int i = 0; i < memInfoSizes.length; i++) {
                    memInfo[i] = memInfoSizes[i] / 1024;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return memInfo;
    }

总结

1)Pss/SharedDirty/Private Dirty三列是读取了/proc/process-id/smaps文件获取的,可以通过adb shell cat /proc/process-id/smaps来查看(需要root)。这是个普通的linux文件,描述了进程的虚拟内存区域的具体信息。

2)Native HeapSize/Alloc/Free三列是使用C函数mallinfo得到的。

3)Dalvik HeapSize/Alloc/Free并非该cpp文件产生,而是android的Debug类生成。

meminfo结果详细分析

Pss对应的TOTAL值:内存所实际占用的值。

Dalvik Heap Size:从RuntimetotalMemory()获得,DalvikHeap总共的内存大小。

Dalvik HeapAlloc:RuntimetotalMemory()-freeMemory() ,Dalvik Heap分配的内存大小。

Dalvik Heap Free:从RuntimefreeMemory()获得,DalvikHeap剩余的内存大小。

Dalvik Heap Size 约等于Dalvik  HeapAlloc+ Dalvik  HeapFree。

Cursor:/dev/ashmem/Cursor Cursor消耗的内存(KB)。

Ashmem:/dev/ashmem,匿名共享内存用来提供共享内存通过分配一个多个进程可以共享的带名称的内存块。

Other dev:/dev/,内部driver占用的在 “Otherdev”。

.so mmap:C 库代码占用的内存。

.jar mmap:Java 文件代码占用的内存。

.apk mmap:apk代码占用的内存。

.ttf mmap:ttf 文件代码占用的内存。

.dex mmap:Dex 文件代码占用的内存。

Other mmap:其他文件占用的内存。
私有(Clean and Dirty)内存:

进程独占的内存。也就是应用进程销毁时系统可以直接回收的内存容量。通常来说,“private dirty”内存是其最重要的部分,因为只被自己的进程使用。它只在内存中存储,因此不能做分页存储到外存(Android不支持swap)。所有分配的Dalvik堆和本地堆都是“private dirty”内存;Dalvik堆和本地堆中和Zygote进程共享的部分是共享dirty内存。

实际使用内存 (PSS):

这是另一种应用内存使用的计算方式,把跨进程的共享页也计算在内。任何独占的内存页直接计算它的PSS值,而和其它进程共享的页则按照共享的比例计算PSS值。例如,在两个进程间共享的页,计算进每个进程PPS的值是它的一半大小。PSS计算方式的一个好处是:把所有进程的PSS值加起来就可以确定所有进程总共占用的内存。这意味着用PSS来计算进程的实际内存使用、进程间对比内存使用和总共剩余内存大小是很好的方式。

通常来说,只需关心Pss Total列和Private Dirty列就可以了。在一些情况下,Private Clean列和Heap Alloc列也会提供很有用的信息。下面是一些应该查看的内存分配类型(行中列出的类型):

  • Dalvik Heap:

应用中Dalvik分配使用的内存。Pss Total包含所有的Zygote分配(如上面PSS定义所描述的,共享跨进程的加权)。Private Dirty是应用堆独占的内存大小,包含了独自分配的部分和应用进程从Zygote复制分裂时被修改的Zygote分配的内存页。注意:新平台版本有Dalvik Other这一项。Dalvik Heap中的Pss Total和Private Dirty不包括Dalvik的开销,例如即时编译(JIT)和垃圾回收(GC),然而老版本都包含在Dalvik的开销里面。

  • Heap Alloc:

是应用中Dalvik堆和本地堆已经分配使用的大小。它的值比Pss Total和Private Dirty大,因为进程是从Zygote中复制分裂出来的,包含了进程共享的分配部分。

  • .so mmap和.dex mmap:

mmap映射的.so(本地) 和.dex(Dalvik)代码使用的内存。Pss Total 包含了跨应用共享的平台代码;Private Clean是应用独享的代码。通常来说,实际映射的内存大小要大一点——这里显示的内存大小是执行了当前操作后应用使用的内存大小。然而,.so mmap 的private dirty比较大,这是由于在加载到最终地址时已经为本地代码分配好了内存空间。

  • Unknown:

无法归类到其它项的内存页。目前,这主要包含大部分的本地分配,就是那些在工具收集数据时由于地址空间布局随机化(Address Space Layout Randomization ,ASLR)不能被计算在内的部分。和Dalvik堆一样, Unknown中的Pss Total把和Zygote共享的部分计算在内,Unknown中的Private Dirty只计算应用独自使用的内存。

  • TOTAL:

进程总使用的实际使用内存(PSS),是上面所有PSS项的总和。它表明了进程总的内存使用量,可以直接用来和其它进程或总的可以内存进行比较。Private Dirty和Private Clean是进程独自占用的总内存,不会和其它进程共享。当进程销毁时,它们(特别是Private Dirty)占用的内存会重新释放回系统。Dirty内存是已经被修改的内存页,因此必须常驻内存(因为没有swap);Clean内存是已经映射持久文件使用的内存页(例如正在被执行的代码),因此一段时间不使用的话就可以置换出去。

  • ViewRootImpl:

进程中活动的根视图的数量。每个根视图与一个窗口关联,因此可以帮助确定涉及对话框和窗口的内存泄露。

  • AppContexts和Activities:

当前驻留在进程中的Context和Activity对象的数量。可以很快的确认常见的由于静态引用而不能被垃圾回收的泄露的 Activity对象。这些对象通常有很多其它相关联的分配,因此这是追查大的内存泄露的很好办法。

注意:View 和 Drawable 对象也持有所在Activity的引用,因此,持有View 或 Drawable 对象也可能会导致应用Activity泄露。

上一篇:Python学习之变量


下一篇:从零开始学习jQuery (四) 使用jQuery操作元素的属性与样式