adb shell dumpsys meminfo packagename
获得如下信息
#应用内存使用,单位KB
Applications Memory Usage (in Kilobytes):
Uptime: 621609791 Realtime: 621609791
##
## Uptime(开机时间):uptime指的是自设备开机以来的累计运行时间,但不包括设备
## 处于深度睡眠(Deep Sleep)状态的时间。也就是说,只有在设备处于活动状态(awake)
## 时,uptime才会增加。在Android开发中,uptime通常用于计算运行时间、执行任务的
## 耗时等。
## Realtime(实时时间):realtime指的是自设备开机以来的累计时间,包括设备
## 处于深度睡眠状态的时间。实际上,realtime是一个连续的、不会被暂停的计时器。
## 在Android开发中,realtime通常用于计算超时、延迟任务、定时任务等。
## pid为805,包名为cn.findpiano.piano的内存信息
## 内存的各个名词的解释,见后面
** MEMINFO in pid 805 [cn.findpiano.piano] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 107179 107140 0 17 215040 109702 105337
Dalvik Heap 17709 17696 0 27 39766 15190 24576
Dalvik Other 14236 14236 0 0
Stack 3480 3480 0 0
Ashmem 156 148 0 0
Gfx dev 16232 8584 0 0
Other dev 60 28 32 0
.so mmap 10837 2484 6324 40
.jar mmap 8 8 0 0
.apk mmap 18547 200 17160 0
.ttf mmap 12 0 0 0
.dex mmap 16568 16 11416 0
.oat mmap 1455 0 560 0
.art mmap 4621 4364 20 4
Other mmap 705 16 312 0
GL mtrack 4196 4196 0 0
Unknown 2914 2908 0 2
TOTAL 219005 165504 35824 90 254806 124892 129913
## 下面是对上面各个细分项目的一个概述
App Summary
Pss(KB)
------
Java Heap: 22080 ## Java堆大小
Native Heap: 107140 ## Native堆大小
Code: 38168 ## 已经加载进地址空间的代码的大小
Stack: 3480 ## native栈大小
Graphics: 12780 ## 图形内存大小,如surface的buffer等
Private Other: 17680 ## 表示没法分类的其他内存区域
System: 17677 ## 由系统使用的内存,比如android系统服务使用
TOTAL: 219005 TOTAL SWAP PSS: 90
Objects
Views: 547 ViewRootImpl: 0
## Views的对象个数,ViewRootImpl的个数。一个Window常有一个ViewRootImpl.
## 因此这个过多,则表示创建了太多的Window比如,Dialog的泄漏等
AppContexts: 5 Activities: 2
## AppContexts和Activity的对象个数,如果这个过大,表示存在Activity泄漏。
## 如Activty里面存在相互之间的静态引用。而这些Activity又有可能持有大内存对象。
Assets: 9 AssetManagers: 4
## Assets和AsstMangers的对象个数。如果过大,则可能存在资源泄漏。
## 可能没有调用相应的close方法
Local Binders: 59 Proxy Binders: 34
## Binder的对象个数。如果它快速变大,可能预示Binder的错误使用
Parcel memory: 25 Parcel count: 102
## Parcel的对象个数和内存,如果它变很大,则意味着,进程间通信使用了大量的数据,
## 或许应该考虑换一种方案
Death Recipients: 3 OpenSSL Sockets: 0
## Death Recipients表示监听远程Binder调用死亡的个数。如果这个数量过大,
## 则有可能预示着Binder对象的泄漏,或者有Binder但是这个是0,则也有问题
WebViews: 0
## WebView的个数
SQL
MEMORY_USED: 4337 ## 已经使用的内存
PAGECACHE_OVERFLOW: 871 MALLOC_SIZE: 117
## 表示页面缓存无法满足的字节大小,其中包括sqlite3_malloc的大小,
## sqlite3_malloc分配的大小由MALLOC_SIZE表示
DATABASES
## 1.pgsz页面大小单位是KB
## 2.dbsz 数据库大小单位是KB
## 3.Lookaside 中被使用过的内存大小 (Lookaside 是一种内存优化策略,
## 可以理解为内存缓存,防止频繁分配内存给系统带来影响)
## 4.cache 它表示的是状态缓存。如第一行:128/157/14 表示缓存页命中了128次,
## 未命中157次,页个数为14个。 可计算总页数为96/4=24页。
## 若未命中次数远远大于命中次数,则可能数据库的建表有很大的问题,
## 这回增加查询时间
pgsz dbsz Lookaside(b) cache Dbname
4 96 109 128/157/14 /data/user/0/cn.findpiano.piano/no_backup/androidx.work.workdb
4 8 0/0/0 (attached) temp
4 96 40 3/19/4 /data/user/0/cn.findpiano.piano/no_backup/androidx.work.workdb (1)
4 56 33 4/50/4 /data/user/0/cn.findpiano.piano/cache/0/record/playrecord.db
4 8 0/0/0 (attached) temp
4 56 65 3/99/4 /data/user/0/cn.findpiano.piano/cache/0/record/playrecord.db (1)
4 56 81 160/15/3 /data/user/0/cn.findpiano.piano/cache/0/record/playrecord.db (2)
4 32 94 59/35/12 /data/user/0/cn.findpiano.piano/databases/zx.db
4 32 103 151/37/14 /data/user/0/cn.findpiano.piano/databases/zx2.db
4 116 32 56/17/3 /data/user/0/cn.findpiano.piano/databases/cg.db
4 72 98 3588/1223/25 /data/user/0/cn.findpiano.piano/databases/pushg3.db
4 356 102 36071/35098/12 /data/user/0/cn.findpiano.piano/databases/find_piano.db
4 8 0/0/0 (attached) temp
4 356 109 55373/28/9 /data/user/0/cn.findpiano.piano/databases/find_piano.db (1)
4 356 73 55435/25/3 /data/user/0/cn.findpiano.piano/databases/find_piano.db (3)
4 356 82 55437/68/4 /data/user/0/cn.findpiano.piano/databases/find_piano.db (2)
4 32 44 9/18/4 /data/user/0/cn.findpiano.piano/databases/gtc3.db
4 20 47 11277/18/4 /data/user/0/cn.findpiano.piano/databases/dim.db
4 32 25 0/18/2 /data/user/0/cn.findpiano.piano/databases/dokit-database
4 8 0/0/0 (attached) temp
4 32 80 3/19/5 /data/user/0/cn.findpiano.piano/databases/dokit-database (1)
4 32 43 0/13/2 /data/user/0/cn.findpiano.piano/databases/dokit-database (2)
4 44 25 1/16/2 /data/user/0/cn.findpiano.piano/databases/pushsdk.db
4 36 96 28/25/11 /data/user/0/cn.findpiano.piano/databases/okdownload-breakpoint.db
4 32 105 4055/11056/25 /data/user/0/cn.findpiano.piano/databases/pushwgs.db
## asset 资源分配
Asset Allocations
zip:/data/app/cn.findpiano.piano-7wIog6gXbNfUWoBFAZ5yoA==/base.apk:/assets/iconfont/iconfont.ttf: 204K
zip:/data/app/cn.findpiano.piano-7wIog6gXbNfUWoBFAZ5yoA==/base.apk:/res/font/sans_regular.otf: 94K
zip:/data/app/cn.findpiano.piano-7wIog6gXbNfUWoBFAZ5yoA==/base.apk:/res/font/sans_regular.otf: 94K
zip:/data/app/cn.findpiano.piano-7wIog6gXbNfUWoBFAZ5yoA==/base.apk:/res/font/sans_medium.otf: 95K
zip:/data/app/cn.findpiano.piano-7wIog6gXbNfUWoBFAZ5yoA==/base.apk:/res/font/sans_medium.otf: 95K
zip:/data/app/cn.findpiano.piano-7wIog6gXbNfUWoBFAZ5yoA==/base.apk:/assets/iconfont/iconfont.ttf: 204K
PSS:这是一种按照比列来划分内存的统计方法。比如一个共享库占1G。由两个进程共享,那么PSS的计算方式就是,将1G一分为2。 分别为512M
按照上图,举例如下:Native Head 按照PSS统计方法,占据107179KB
Private Dirty:其中Private表示只有本进程可访问。Dirty表示还没有写回磁盘。举例如下:如上图第一行表示,分配给本进程的,已经被修改,但未写入磁盘的Native Heap大小为107140KB。
Private Clean: 其中Private表示只有本进程可访问。Clean表示,已经写回磁盘。举例:如上图第一行表示,分配给本进程的,已经写入磁盘的Native Heap为0
SwapPss Dirty:表示,将物理内存中的部分内容,放入swap中。因为内存紧张,把暂时用不到的东西放入Swap区域中。举例,如上图第一行,Native Heap放入Swap区域的
有17KB。其中Pss表示的就是上面介绍的按比例统计方法。可见如果SwapPss一直在更改,或者变大,说明设备进入了内存紧张的阶段,或者说,这个应用有毛病,如果swap一直变化很大,也说明出现内存抖动
注意:Android的swap并不像其他操作系统一样,将内存信息,交换到外存中。它是将内存暂时不用的区域,压缩之后,放入另外一个内存区域中。这个内存区域叫做zRam. zRam不是固定大小的,它会动态变化。只有当zRam都没法放下时,就会触发LMK,叫做low memory killer守护进程,进行应用的清理,滕出空间。
Heap Size:表示堆的大小,堆是动态分配内存的地方。举例。如上图第一行,表示:分配给Native Heap的堆大小为215040KB。
Heap Alloc和Heap Free:表示堆中已经分配和未分配的大小分别是多少。这两者之和就是Heap Size的大小。
NativeHeap 和Dalvik Heap:表示的是动态分配内存的区域。其中Dalvik Heap表示的java虚拟机使用的动态内存分配区域,如new 一个对象。剩下的就是NativeHeap,如c/c++分配的内存等等。举例,第一行第一列。按照Pss统计,Native Heap的大小为107179KB,其中只能被本应用使用的大小有107140KB,因为堆不需要写入磁盘即手机的Flash中,所以,Private Clean为0。而写入Swap区域的有17KB
可以看到Pss的数据量,大于Private Dirty的数据量,主要有如下的原因:native heap 含有被其他应用进程共享的部分。主要有两部分。1. 共享的so库,如libc,openGL所占用的heap空间。2.Binder在内核中分配的内存,也会被划入多个进程中。
除此之外,对于java的Bitmap对象,可能会看见Native Heap被大量增加,而Dalvik Heap少量增加的现象,这是因为Bitmap对象会在native heap中存储对应的数据
Dalvik Other:表示除了heap之外的其他内存。比如类,方法表,线程栈等等。举例如下:当java虚拟机需要加载一个类的时候,先从压缩包里面读出数据,然后分配内存,将这个类对应的元数据放入这部分内存中。这部分内存就会划入Dalvik other中
Stack:表示的是native的栈的大小。Stack是方法调用进行本地变量存储的区域。如果Stack过大,可能存在过多的线程,也可能存在深度递归操作
需要注意的是,java也有线程Stack,不过这部分被划分在Dalvik heap中
Ashmem:这是android独有的,匿名共享内存区域。即这部分可以被其他进程共享。如 1. Binder创建的数据传输区域。2.android的图形缓冲区域(早期android才会使用,现在android使用Graphic Buffer)3.共享的内存数据结构,如PackageManager会使用Ashmem维护一个所有应用程序的列表,这样可以被任何应用进程访问。4. WebView:Ashmem被用于存储共享的图片,字体等资源,这样可以避免每个进程都加载这些资源
在Ashmem中提到了Binder,在Native Heap中也提到了Binder。那么Binder的哪些数据属于Native Heap,那些属于Ashemem?
Binder的运行时环境,如Binder服务,Binder对象,Binder分配的数据结构,都属于Native Heap中。
Binder大量传输数据时,使用的Ashmem,则属于Ashemem
Gfx dev:表示图形设备,使用的内存,如存储纹理,顶点,帧缓冲等等。这部分内存由GPU程序来分配和管理,应用程序使用特定的API,如opengl或vulkan,进行交互。这部分内存可能是专用内存,也可能是通用内存的一部分,这取决于驱动的实现,android一般情况下是通用内存
Other dev:表示其他设备,使用的内存,如编解码器,这部分内存由驱动程序进行管理和分配,同Gfx dev相同它可能是专有内存,也可能是通用内存的一部分。如果这个数值过大,可能表示应用程序使用了过多的资源,甚至是使用完成之后,没有释放
xxx mmap:表示映射xxx所使用的内存大小。举例如下:.so mmap 表示so库映射大小为10837KB。但是需要注意的是:oat和art映射部分,这两部分仅仅表示所有应用共享的oat文件和art文件
何为oat文件,art文件,dex文件,odex文件,vdex文件?
android为了更快的运行字节码,对字节码进行了优化,优化之后的代码保存为dex格式。dex优化包含:字节码压缩,dex使用LEB128的压缩算法,为了适应移动设备的小容量存储。寄存器分配优化:因为Dlavik虚拟机的寄存器被设计的很少,为了在少量寄存器下的高速执行,需要对字节码进行优化
还可针对dex文件进行进一步的优化,这个过程可以发生在编译时候,也可以发生在安装的时候,将dex优化为odex。这些优化包含有:方法调用优化,类加载验证优化等等
android 5.0之后,为了更彻底的优化dex,在编译时候,或者安装时候,将dex直接转换机器指令,并将其保存在oat文件中。而机器码因架构的不同而不同。同时针对同一架构的不同型号cpu,还可以有更进一步的优化。所以oat文件会在不同设备之间表现一定的差异。因此有了oat文件之后,其实并不需要odex文件,但为了兼容以前的版本,依然允许运行odex文件
而上述的.oat mmap即为所有应用程序共用的oat文件的映射内存大小。它不包括本应用进程独有的oat文件的映射。如将android的framework部分,优化之后放在system分区的framework/arm64/boot-framework.oat下面。应用如果需要使用framework中的api,就会将内存映射到这部分,并修改.oat mmap的映射大小。如果是本应用的oat,是表现上图的哪一块区域呢?答案是没有表现,后面会通过读取/proc/pid/mmap来查看
art文件则是对一些内部实现的直接表示,如一些字符串,如果要新建一个字符串对象,可能需要在堆中重新分配内存。但是有些对象是一直存在且不变的。此时可以将其打包成.art文件,并在使用时通过mmap映射即可
而上图中的.art mmap则表示的是所有应用程序共用的art文件部分。
other mmap:则表示其他的内存映射,比如使用的其他设备空间等等。
GL mtracks:代表了由 OpenGL ES 图形库所使用的内存大小,常常由GPU驱动来管理。他与GFx dev之间的区别就是,Gfx dev表示的是图形设备使用的内存,而GL mtracks表示是open gl 使用的内存。如果分配大量内存,则可以看见Gfx dev的内存会远小于GL mtracks.
Unkonwn:无法进行分类的内存数据,这部分数据,通常是native分配的。如虚拟机自身使用的堆空间
## root权限
adb shell cat /proc/pid/maps > maps-pid.log
或者使用bugreportz
## 使用bugreportz
adb shell bugreportz
##pull 出对应的zip包,解压之后,会看到fs/data/proc找到对应的pid文件夹,
## 就能看到里面的maps文件,即为对应的内存映射文件
先打开一例,叙述如下:
##每一行,都有六列,分别表示如下
## 1. 7d5380000-7d6390000:该段内存区域的起始地址和结束地址
## 2. rw-s:该段区域的权限。r-读,w-写,x-执行,s-共享,p-私有。
## 如果没有则用短横线代替“-”
## 3. 0035f000:该区域的偏移地址
## 4. 00:0e:表示该设备文件的主设备号和次设备号
## 5. 15754:该文件的文件节点号
## 6. /dev/kgsl-3d0:文件的名字
7d5380000-7d6390000 rw-s 0035f000 00:0e 15754 /dev/kgsl-3d0
那么有没有这段内存的详细描述呢?答案是有的。在同一个目录的smaps文件中,如下
12c00000-13bc0000 rw-p 00000000 00:01 16726 /dev/ashmem/dalvik-main space (region space) (deleted)
Size: 16128 kB
## 大小
Rss: 10228 kB
## Rss统计下的大小
Pss: 10228 kB
## Pss统计下的大小
Shared_Clean: 0 kB
## 共享的,已经写回闪存的大小
Shared_Dirty: 0 kB
## 功效的,已经修改但还未写回闪存的大小
Private_Clean: 0 kB
## 同shared_clean,但是是私有的
Private_Dirty: 10228 kB
## 同shared_dirty,但是是私有的
Referenced: 10228 kB
## Referenced 字段显示了该内存区域中被操作系统认为正在使用(被引用)的内存数量
Anonymous: 10228 kB
## 表示匿名,即没有特定的文件与之对应,完完全全是内存中的。
AnonHugePages: 0 kB
## 表示匿名巨页的大小。巨页是比常规内存的页大很多的页,这样可以,
## 减少页表项,提高地址转换效率
Swap: 0 kB
## 内存被放入swap区域的大小
SwapPss: 0 kB
## 内存被放入swap区域的大小,按Pss统计
KernelPageSize: 4 kB
## 内核页大小
MMUPageSize: 4 kB
## MMU页大小
Locked: 0 kB
## 已经锁定的大小
VmFlags: rd wr mr mw me ac
## 内存标志,具体的内存标志,还有很多我不是很熟悉,不过可以直接查看linux操作手册。man proc进行查看,现摘录如下:
# rd - readable
# wr - writable
# ex - executable
# sh - shared
# mr - may read
# mw - may write
# me - may execute
# ms - may share
# gd - stack segment grows down
# pf - pure PFN range
# dw - disabled write to the mapped file
# lo - pages are locked in memory
# io - memory mapped I/O area
# sr - sequential read advise provided
# rr - random read advise provided
# dc - do not copy area on fork
# de - do not expand area on remapping
# ac - area is accountable
# nr - swap space is not reserved for the area
# ht - area uses huge tlb pages
# sf - perform synchronous page faults (since Linux 4.15)
# nl - non-linear mapping (removed in Linux 4.0)
# ar - architecture specific flag
# wf - wipe on fork (since Linux 4.14)
# dd - do not include area into core dump
# sd - soft-dirty flag (since Linux 3.13)
# mm - mixed map area
# hg - huge page advise flag
# nh - no-huge page advise flag
# mg - mergeable advise flag
# um - userfaultfd missing pages tracking (since Linux 4.3)
# uw - userfaultfd wprotect pages tracking (since Linux 4.3)
android中常见的一些特殊文件名字的意义:
12c00000-13400000 rw-p 00000000 00:01 9000 /dev/ashmem/dalvik-main space (region space) (deleted)
## 1. /dev/ashmem 是 Android 特有的一种匿名共享内存接口,
## 它提供了一种在进程之间共享内存的方式
## 2. dalvik-main space 是指这个内存区域被 Dalvik 虚拟机
## (或 ART,它是 Dalvik 的后续版本)用作其主内存空间。主要用于存储 Java 对象。
## 3. (region space) 表示这个内存区域采用了 "region space" 的内存管理策略。
## 这是 Android 8.0(API 级别 26)引入的一种新的内存管理策略,它将内存分为
## 多个相同大小的区域
## 4. delete 表示文件已经被删除,这里是虚拟设备,不存在文件,故为delete
## 5. 因此,/dev/ashmem/dalvik-main space (region space) 这个条目表示,
## 这个内存区域是由 Dalvik 或 ART 虚拟机使用的,用作其主内存空间,
## 存储 Java 对象,并采用了 "region space" 的内存管理策略。
73c1b000-73ca0000 rw-p 00000000 00:01 8997 /dev/ashmem/dalvik-zygote space (deleted)
## 在fork子进程之前,zygote的堆空间
77c1b000-97c1b000 rw-p 00000000 00:01 9002 /dev/ashmem/dalvik-free list large object space (deleted)
## 这是创建java大对象的内存区域
上面是一些常见的内存空间,后面有机会,再来介绍虚拟机内部的空间划分和排布规则
708b4000-708c1000 rw-p 00000000 08:0a 761948 /data/dalvik-cache/arm64/system@framework@*
## 表示使用java虚拟机缓存下的各种文件,如art
71327000-7132a000 r--p 00000000 103:02 1459 /system/framework/arm64/*
## 表示映射的是system/framwork下面的文件,如各种服务编译之后的oat
71b47000-71b49000 rw-p 00000000 00:00 0 [anon:.bss]
## 1. anon表示匿名。.bss表示静态数据段。表示程序中使用的,未初始化的,
## 全局静态变量和全局外部变量。
## 2. .bss既可以是动态分配,也可以是静态编译好的。此处的anon即表示为动态分配的。
7185a00000-7193a00000 rw-p 00000000 00:00 0 [anon:libc_malloc]
## 为c/c++代码使用malloc和new分配内存的区域
717ce2e000-717ce2f000 ---p 00000000 00:00 0 [anon:thread signal stack guard page]
717ce2f000-717ce33000 rw-p 00000000 00:00 0 [anon:thread signal stack]
717ce33000-717ce34000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
717ce34000-717ce37000 rw-p 00000000 00:00 0 [anon:bionic TLS]
717ce37000-717ce38000 ---p 00000000 00:00 0 [anon:bionic TLS guard]
717ce38000-717ce39000 ---p 00000000 00:00 0 [anon:thread stack guard page]
717ce39000-717cf35000 rw-p 00000000 00:00 0 [stack:9166]
## 1.这是一块连续的内存区域,最底下的是线程的堆栈区域[stack:9166]
## 其中9166是对应的线程id
## 2.stack:9166上面则是,线程堆栈区域的保护区域
## 3.再上面是线程的本地存储区域(Thread local storeage,TLS).
## 在TLS的上下,分别有一块保护区域,叫做TLS guard
## 4.在TLS的上面,则是这个线程的信号堆栈区域,signal stack
## 5.同样,在signal stak上面有保护区域,叫做signal stack guard page
## 6.保护区域是为了防止溢出,一旦访问到保护区域,则程序报错
72bd43f000-72bd440000 r--p 00000000 00:00 0 [vvar]
72bd440000-72bd441000 r-xp 00000000 00:00 0 [vdso]
## [vdso]:虚拟动态共享对象(Virtual Dynamic Shared Object)是一种内
## 核机制,允许在用户空间的应用程序直接访问内核提供的某些功能,
## 从而避免系统调用的开销。VDSO 包含一些在内核态和用户态之间共享的函数,
## 通常用于实现高效的时间相关系统调用,
## 例如 gettimeofday() 和 clock_gettime()。VDSO 的主要目的是减少系统
## 调用的开销,从而提高程序的性能。
## [vvar]:虚拟变量(Virtual Variable)是一种内核提供的只读内存区域,
## 用于存储与时间相关的内核变量。
## 这些变量通常包括实时时钟(RTC)以及内核的时间戳计数器(TSC)。用户空间
## 的应用程序可以通过 [vvar] 内存区域直接访问这些变量,
## 从而提高对时间相关信息的访问速度。
注意:若发现[stack:pid]这些区域太多,则可能需要考虑线程是否溢出。如线程没有正确的关闭,或者创建了太多的临时线程等
7d5380000-7d6390000 rw-s 0035f000 00:0e 15754 /dev/kgsl-3d0
## 表示映射的是/dev/kgsl-3d0设备,这是一个GPU设备。按照规则,
## 往这个区域写入数据,就可以操作GPU
717cbcc000-717cbcd000 r--p 00000000 08:0a 6678559 /data/media/0/download/others.$~&FochIvRkczjco7HwvK80VNSWqX4Z.png
## 717cbcc000-717cbcd000这个区域对应的内容,就是png中的内容
注意:如果发现这种区域过多,则需要考虑是否有重复加载,或者问加你没有正确关闭等。
第一篇主要完成了,内存总览,包括一些基本概念
下一篇,将介绍如何查看内存中的细节,如某个指针对应的内容是什么,堆栈里面的数据有哪些等等