Android 内存之监控篇
一、前言
内存问题主要会造成如下几个影响。第一、会发生OOM等异常,因为每个对象都会占用一定的内存,内存过多会影响对象的分配失败,严重者会导致设备重启。第二、内存过大,导致系统回收缓存内存,以及会加快GC频率,从而导致应用卡顿。
内存的监控就尤为的重要。下面从几个方面来看Android对内存的监控,从而有助于观察内存变化。
二、free
free是轻量级的查看设备整体内存情况,具体例子如下:
root@debian7:/proc/10# free
total used free shared buffers cached
Mem: 3044840 247692 2797148 0 19896 110084
-/+ buffers/cache: 117712 2927128
Swap: 901116 0 901116
其中total = used + free,单位KB。
对于-/+ buffers/cache行,是从有无缓冲来看。117712 = used - buffers - cached.
2927128 = free + buffers + cached.
三、/proc/meminfo文件
/proc/meminfo是free的加强版,free中的数据也是从/proc/meminfo而来的。
root@debian7:/proc/10# cat /proc/meminfo
MemTotal: 3044840 kB // RAM内存总大小
MemFree: 2797520 kB // RAM可用内存大小
Buffers: 19912 kB // Buffers缓存,文件缓存
Cached: 110084 kB // Cached缓存大小
SwapCached: 0 kB
Active: 97184 kB // 在活跃下的缓冲或高速缓冲存储器页面文件的大小
Inactive: 94008 kB // 非活跃下的缓冲或高速缓冲存储器页面文件的大小
Active(anon): 61312 kB // Active = Active(anon) + Active(file)
Inactive(anon): 6008 kB
Active(file): 35872 kB
Inactive(file): 88000 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 901116 kB
SwapFree: 901116 kB
Dirty: 0 kB // 等待被写回到磁盘的内存大小。
Writeback: 0 kB // 正在被写回到磁盘的内存大小。
AnonPages: 61188 kB
Mapped: 24656 kB // 文件通过mmap分配的内存
Shmem: 6132 kB
Slab: 20412 kB // 内核数据结构缓存的大小。Linux中的Slab内存分配策略,相对于伙伴系统分配
SReclaimable: 6288 kB
SUnreclaim: 14124 kB
KernelStack: 1000 kB
PageTables: 4656 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2423536 kB
Committed_AS: 535828 kB
VmallocTotal: 34359738367 kB // 总分配的虚拟地址空间
VmallocUsed: 158760 kB // 已分配的虚拟地址空间
VmallocChunk: 34359576572 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 45056 kB
DirectMap2M: 2076672 kB
DirectMap1G: 1048576 kB
四、vmstat
vmstat命令可以查看内存、IO和CPU等信息。
语法命令:
Usage: vmstat [ -n iterations ] [ -d delay ] [ -r header_repeat ]
-n iterations 数据循环输出的次数
-d delay 两次数据间的延迟时长(单位:S)
-r header_repeat 循环多少次,再输出一次头信息行
vmstat例子:
root@debian7:/proc/10# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
0 0 0 2797396 19960 110084 0 0 1 0 13 25 0 0 100 0
procs 程序
- r : 运行的进程数量
- b :等待IO阻塞的进程数量
memory 内存
- swpd : 虚拟内存(swap空间)已使用的大小
- free :剩余的物理内存大小
- buff : buff缓存大小
- cache :文件等cache大小
swap swap空间,内存够用时,si和so值都为0
- si : swap空间写入内存的数据量;
- so: 内存写入swap空间的数据量;
IO
- bi : 每秒从块设备读取块的数量
- bo:每秒向块设备写入的块数量
system
- in : 每秒的中断次数
- cs :等秒的上下文切换次数
cpu
- us : 用户态执行时间
- sy : 内核态执行时间
- id : 空闲时间(包括IO等待时间)
- wa : 等待IO时间
五、/proc/[PID]/status
root@p212:/data/dropbear # cat /proc/4943/status
Name: XXXXX
State: S (sleeping) : 状态
Tgid: 4943 :线程组ID
Pid: 4943 :进程ID,同TGID,说明是主线程
PPid: 3769 :父进程ID
TracerPid: 0
Uid: 10039 10039 10039 10039
Gid: 10039 10039 10039 10039
Ngid: 0
FDSize: 64 :FDSize是当前分配过的文件描述符数量
Groups: 3003 9997 50039 : groups表示启动这个进程的用户所在的组.
VmPeak: 1503968 kB :当前进程运行过程中占用内存的峰值
VmSize: 878224 kB :虚拟内存大小
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 40040 kB :分配到物理内存的峰值
VmRSS: 38120 kB : 虚拟内存驻留集合大小
VmData: 134084 kB :进程数据段的大小
VmStk: 8196 kB : 进程堆栈段的大小
VmExe: 20 kB
VmLib: 69224 kB
VmPTE: 440 kB
VmSwap: 0 kB : 进程占用Swap的大小.
Threads: 22 :线程数量
SigQ: 0/2462
SigPnd: 0000000000000000 : 存储了该线程的待处理信号
ShdPnd: 0000000000000000 : 存储了该线程组的待处理信号
SigBlk: 0000000000001204 : 存放被阻塞的信号
SigIgn: 0000000000000000 : 存放可被忽略的信号
SigCgt: 00000002000094f8
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000000000000000
Seccomp: 0
Cpus_allowed: f
Cpus_allowed_list: 0-3
voluntary_ctxt_switches: 358 : 进程主动切换上下文的次数(资源得不到等)
nonvoluntary_ctxt_switches: 400 : 进程被动切换上下文的次数.
六、Android#Runtime
Android中提供了一些接口,供使用者调用,可定时的采集以下几种内存情况,从而判断当前进程内存情况。
Runtime runtime = Runtime.getRuntime();
long javaMax = runtime.maxMemory(); // JVM可分配的最大内存
long javaTotal = runtime.totalMemory(); // 当前分配的内存
long javaUsed = javaTotal - runtime.freeMemory(); // 当前使用的内存
float proportion = (float) javaUsed / javaMax;
Log.e("TAG", "onResume: javaMax="+javaMax+";javaTotal="+javaTotal+";javaUsed="+javaUsed+";proportion="+proportion);
七、Android#onLowMemory
在Android4.0中提供了一些监听内存的接口OnLowMemory和onTrimMemory
【一】、OnLowMemory
OnLowMemory是ComponentCallbacks接口中的方法,当系统内存不足,要被杀死后台程序时,会调用该方法。
可用在Application、Activity、Fragement、Service和ContentProvider中
【二】、onTrimMemory
因为OnLowMemory的接口太简单了,并没有提供内存的状态,在ComponentCallbacks2中丰富了回调了接口。
public interface ComponentCallbacks2 extends ComponentCallbacks {
/** @hide */
@IntDef(prefix = { "TRIM_MEMORY_" }, value = {
TRIM_MEMORY_COMPLETE,
TRIM_MEMORY_MODERATE,
TRIM_MEMORY_BACKGROUND,
TRIM_MEMORY_UI_HIDDEN,
TRIM_MEMORY_RUNNING_CRITICAL,
TRIM_MEMORY_RUNNING_LOW,
TRIM_MEMORY_RUNNING_MODERATE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface TrimMemoryLevel {}
static final int TRIM_MEMORY_COMPLETE = 80;
static final int TRIM_MEMORY_MODERATE = 60;
static final int TRIM_MEMORY_BACKGROUND = 40;
static final int TRIM_MEMORY_UI_HIDDEN = 20;
static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;
static final int TRIM_MEMORY_RUNNING_LOW = 10;
static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
void onTrimMemory(@TrimMemoryLevel int level);
}
同onLowMemory一致,也可用在Application、Activity、Fragement、Service和ContentProvider中。
- TRIM_MEMORY_COMPLETE:内存不足,并且该进程在后台进程列表最后一个,马上就要被清理
- TRIM_MEMORY_MODERATE:内存不足,并且该进程在后台进程列表的中部。
- TRIM_MEMORY_BACKGROUND:内存不足,并且该进程是后台进程。
- TRIM_MEMORY_UI_HIDDEN:内存不足,并且该进程的UI已经不可见了。
- TRIM_MEMORY_RUNNING_CRITICAL:内存不足,并且该进程不是消耗性的后台进程,需要清理内存
- TRIM_MEMORY_RUNNING_LOW:内存不足,并且该进程不是消耗性的后台进程,需要清理内存
- TRIM_MEMORY_RUNNING_MODERATE:内存不足,并且该进程不是消耗性的后台进程,需要清理内存
八、dumpsys
通过adb shell dumpsys meminfo [pid | 包名] 可以查看单个APP内存情况
如下:
Applications Memory Usage (kB):
Uptime: 31564143 Realtime: 31564143
** MEMINFO in pid 30712 [XXX] **
Pss Private Private Swapped Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 2196 2092 0 0 6144 4855 1288
Dalvik Heap 1517 1396 0 0 3873 2449 1424
Dalvik Other 373 372 0 0
Stack 132 132 0 0
Ashmem 2 0 0 0
Other dev 5 0 4 0
.so mmap 1148 96 156 0
.apk mmap 326 0 28 0
.ttf mmap 88 0 76 0
.dex mmap 2844 4 2840 0
.oat mmap 1204 0 164 0
.art mmap 1321 476 368 0
Other mmap 11 8 0 0
GL mtrack 2960 2960 0 0
Unknown 121 120 0 0
TOTAL 14248 7656 3636 0 10017 7304 2712
App Summary
Pss(KB)
------
Java Heap: 2240
Native Heap: 2092
Code: 3364
Stack: 132
Graphics: 2960
Private Other: 504
System: 2956
TOTAL: 14248 TOTAL SWAP (KB): 0
Objects
Views: 15 ViewRootImpl: 1
AppContexts: 2 Activities: 1
Assets: 2 AssetManagers: 2
Local Binders: 9 Proxy Binders: 12
Parcel memory: 3 Parcel count: 14
Death Recipients: 0 OpenSSL Sockets: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
第一部分主要字段解析:
- Native Heap : Native堆大小
- Dalvik Heap : java堆大小
- Stack :栈大小
- Ashmem : 匿名共享内存大小
- .so mmap : 映射的so库大小
- .apk mmap:apk占用内存大小
- .dex mmap : dex占用内存大小
- Pss Total :物理内存总大小
- Private Dirty : 进程私有的内存,相对磁盘数据有改动的内存
- Private Clean : 进程私有的内存,相对磁盘数据没有修改的内存
- Heap Size : Dalvik中,同runtime.totalMemory()
- Heap Alloc : Dalvik中,同runtime.totalMemory()-runtime.freeMemory()
- Heap Free : Dalvik中,同runtime.freeMemory()
App Summary主要字段解析:
- Java Heap : Dalvik Heap 中的 Private Dirty + .art mmap 的 Private Dirty + Private Clean
- Native Heap :Native Heap
- Code : .so mmap+ .jar mmap + .apk mmap + .ttf mmap + .dex mmap + .oat mmap的 Private Dirty + Private Clean
- Stack :Stack
Objects主要字段解析:
- Views :存活的view的个数
- ViewRootImpl : 存活的ViewRootImpl数量
- AppContexts :APP整个上下文数量
- Activities : 存活的Activity数量