一、内存指标
Item
全称
含义
等价
USS
Unique Set Size
物理内存
进程独占的内存
PSS
Proportional Set Size
物理内存
PSS= USS+ 按比例包含共享库
RSS
Resident Set Size
物理内存
RSS= USS+ 包含共享库
VSS
Virtual Set Size
虚拟内存
VSS= RSS+ 未分配实际物理内存
内存的大小关系:VSS >= RSS >= PSS >= USS
二、常用内存分析命令
1 procrank
获取所有进程的内存使用的排行榜,排行是以Pss的大小而排序,能输出详细的VSS/RSS/PSS/USS内存指标。
PID Vss Rss Pss Uss Swap PSwap USwap ZSwap cmdline
1479 2748552K 382328K 224152K 168908K 26040K 16185K 15944K 3575K system_server
...
1817679K 1618480K 1530116K 470311K 433840K 103909K TOTAL
ZRAM: 111212K physical used for 503364K in swap (1048572K total swap)
RAM: 2914764K total, 41284K free, 11904K buffers, 592500K cached, 11672K shmem, 183752K slab
2 free
查看可用内存,缺省单位KB。该命令比较简单、轻量,专注于查看剩余内存情况。数据来源于/proc/meminfo。
total used free shared buffers
Mem: 2984718336 2946801664 37916672 11943936 11337728
-/+ buffers/cache: 2935463936 49254400
Swap: 1073737728 515416064 558321664
对于Mem行,存在的公式关系: total = used + free;
对于-/+ buffers行:buffers/cache used = mem used - mem buffers
buffers/cache free = mem free + mem buffers
3 cat /proc/meminfo
展示的是系统整体内存情况,内存项按类型进行分类:
MemTotal: 2914764 kB 内存总数 (除去BIOS和内核预留的内存,剩下可供系统支配的内存)
MemFree: 78008 kB 系统空闲内存(系统尚未被使用的,total-free = used)
MemAvailable: 440972 kB 可用内存(memfree + 可回收内存(部分buffer/cached,slab也能回收一部分))
Buffers: 14200 kB 缓冲区内存(给文件做缓冲的)
Cached: 566648 kB 缓存区内存(被高速缓冲存储器(cache memory)用的内存)
SwapCached: 60560 kB 缓存区中已经被交换出来的内存
Active: 1243832 kB 在活跃使用中的缓冲或高速缓冲存储器页面文件的大小,除非非常必要否则不会被移作他用
Inactive: 576108 kB 在不经常使用中的缓冲或高速缓冲存储器页面文件的大小,可能被用于其他途径
Active(anon): 1043860 kB
Inactive(anon): 377368 kB
Active(file): 199972 kB
Inactive(file): 198740 kB
Unevictable: 173940 kB
Mlocked: 173940 kB 被系统锁定的页面
SwapTotal: 1048572 kB 交换空间的总大小(设置的zram交换空间大小)
SwapFree: 471124 kB 未被使用交换空间的大小
Dirty: 72 kB 等待被写回到磁盘的内存大小
Writeback: 0 kB 正在被写回到磁盘的内存大小
AnonPages: 1407356 kB 未映射页的内存大小
Mapped: 435656 kB 已经被设备和文件等映射的内存大小
Shmem: 8388 kB 共享内存大小
Slab: 176044 kB 内核中slab分配的内存大小(slab = SReclaimable+SUnreclaim)
SReclaimable: 55528 kB 可收回Slab的内存大小
SUnreclaim: 120516 kB 不可收回Slab的内存大小
KernelStack: 56144 kB 内核栈区大小
PageTables: 61256 kB 内存管理的分页的页表大小
NFS_Unstable: 0 kB 不稳定页表的大小
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2505952 kB
Committed_AS: 95946620 kB
VmallocTotal: 258998208 kB 可以被vmalloc分配的虚拟内存大小
VmallocUsed: 142860 kB 已经被使用的虚拟内存大小
VmallocChunk: 258740196 kB
日常使用经验总结:
1)MemTotal: 是除去系统底层预留内存之外,能被系统使用的总内存大小,正常情况下会比实际内存小一点,但是如果小太多的话属于预留不合理,需要看看原因,这属于先天不足,天花板过低。
2)MemFree 、MemAvailable: 前者是当前系统未被使用的内存,后者是当前系统可以被使用的内存(包括可以被回收的部分内存),意思就那个什么,挤挤还是有的。从经验上来说,如果当前MemAvailable底于总内存的1/10,那么系统可能会出现因为内存造成的卡顿,其中原因可能包括频繁回收内存造成的阻塞、耗时以及寻址难度加大变相地增加了内存分配的时间等等。
3)Buffers 、Cached: 前者用于缓存磁盘blocks以优化block I/O,后者用于缓存文件内容以优化文件I/O。部分内存是可被回收的,被算到MemAvailable中。MemAvailable ≈ MemFree+Buffers+Cached。
4)Mlocked: 被系统锁定的页面。比如系统中google 7.0加的PinnerService 就会有这个效果,把常用的内容锁定在内存中,避免频繁的内存回收与分配,优化但不限于提升io效率。典型的用空间换时间,如果是Android 2G及其以下内存的手机,建议关闭PinnerService,要啥自行车。PinnerService官方描述
5)SwapTotal、SwapFree: 这就zram,如果为0就是没打开。SwapTotal是zram总空间大小,SwapFree是没交换的空间大小。
6)Slab: 内核基于Buddy做了page的粗分,Slab基于Buddy做了内存二次划分,这部分就是基于Slab内存分配的内存大小。使用的函数是kmalloc/kfree。 SReclaimable和SUnreclaim分别是Slab的可回收和不可回收部分(slab = SReclaimable+SUnreclaim),如果Slab比较大,可能是kernel debug开关被打开了(也不一定,具体看调试内容),而且SUnreclaim也非常大的话,可能存在kernel泄漏。
7)Kernel内存可使用内存 ≈ Slab + KernelStack + PageTables。
4 dumpsys meminfo
4.1 dumpsys实现逻辑简单介绍
dumpsys的源码结构其实很简单,只有一个dumpsys.cpp
源码路径是:/frameworks/native/cmds/dumpsys/dumpsys.cpp
在其main方法中,先通过defaultServiceManager()函数获得ServiceManager对象,然后根据dumpsys传进来的参数通过函数checkService来找到具体的service, 并执行该service的dump方法,达到dump service的目的。
4.2 dumpsys meminfo 数据组成
dumpsys meminfo对应的服务是:ActivityManagerService, 它从memBinder类的dump函数开始执行的。
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump meminfo from from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " without permission " + android.Manifest.permission.DUMP);
return;
}
mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
}
}
主要收集内存信息并打印是在dumpApplicationMemoryUsage方法中做的,但是该方法比较大,就不列出来了,举个例子:
Debug.getMemoryInfo(pid, mi);//通过debug.java中的getMemoryInfo函数来获取当前进程的整体memory信息,获取的是对应的/proc/$/smaps文件统计出来的信息。getMemoryInfo是个native方法,对应到android_os_Debug.cpp的android_os_Debug_getDirtyPagesPid。
从代码看:这部分内容是从/proc/$/smaps获取的。
那么总结下dumpsys meminfo的出处:
很明显,dumpsys meminfo 获取的数据是从系统各个渠道汇集来的。
4.3 dumpsys meminfo 展示的是系统整体内存情况, 内存项按进程进行分类
Total PSS by process: Java层存活的进程及其占用内存情况
241,086K: system (pid 1479)
161,423K: surfaceflinger (pid 544)
137,754K: com.android.systemui (pid 4843 / activities)
...
Total PSS by OOM adjustment: Native存活的进程及其占用内存情况
376,783K: Native
161,423K: surfaceflinger (pid 544)
14,303K: audioserver (pid 725)
9,247K: zygote (pid 719)
...
576,007K: Persistent 按进程优先级分别来统计对应的进程及其内存使用情况, 列举几个:
241,086K: system (pid 1479)
...
219,381K: Foreground
167,657K: com.tengxin.youqianji (pid 29421 / activities)
...
317,970K: B Services
33,115K: com.UCMobile:channel (pid 25225)
...
410,541K: Cached
36,294K: com.android.vending (pid 13418)
...
Total PSS by category: 按类型进行分类
555,972K: Dalvik
401,562K: Native
154,436K: EGL mtrack
151,504K: Unknown
145,638K: .oat mmap
118,919K: .dex mmap
118,259K: .art mmap
88,952K: GL mtrack
73,583K: Dalvik Other
51,418K: Gfx dev
39,993K: .so mmap
28,950K: .apk mmap
27,075K: Stack
9,919K: Ashmem
6,219K: Other mmap
1,968K: Other dev
120K: .jar mmap
80K: .ttf mmap
16K: Cursor
0K: Other mtrack
Total RAM: 2,914,764K (status normal)
Free RAM: 1,208,341K ( 410,541K cached pss + 730,784K cached kernel + 2,676K cached ion + 64,340K free)
Used RAM: 2,326,986K (1,994,786K used pss + 332,200K kernel)
Lost RAM: -16,504K
ZRAM: 101,812K physical used for 466,040K in swap (1,048,572K total swap)
Tuning: 192 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
先总结下最下面的统计:
Total RAM:内存总数,与proc/meminfo中的MemTotal一致。
Free RAM:cached pss + cached kernel + cached ion + free 手机剩余内存一般是看它
cached pss:dumpsys meminfo中 cached 进程的PSS总和
cached kernel:这个暂时无定论
free:proc/meminfo 的MemFree
ion cached以及gpu cached:display相关ion的内存占用
Used RAM:used pss+kernel+trace buffer+ion display+cma usage
used pss:native process PSS+dumpsys meminfo APP除cached部分的PSS总和
kernel:meminfo的Shmem+Slab+PageTables+kernelStack+vmallocinfo里面的ioremap项+map_lowmem项所占内存的和
ion disp:display相关的ion模块内存占用
cma usage:cma模块占用
Lost RAM:与cache ion有关
ZRAM:zram swap转换情况
Tuning:这一行主要是system的一些设置,没看过
5 dumpsys meminfo [pid | packageName]
查看单个进程内存详情
** MEMINFO in pid 1479 [system] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 26106 25984 104 8739 73728 34267 39460
Dalvik Heap 63706 63676 4 1858 56528 40144 16384
Dalvik Other 6704 6668 12 24
Stack 2388 2364 24 1352
Ashmem 8004 8000 0 0
Gfx dev 532 124 0 0
Other dev 55 8 36 0
.so mmap 1561 444 780 488
.jar mmap 0 0 0 8
.apk mmap 145 0 0 0
.dex mmap 28702 0 4428 40
.oat mmap 78494 0 49992 0
.art mmap 3068 2684 76 856
Other mmap 34 4 0 0
GL mtrack 1424 1424 0 0
Unknown 1275 1084 188 2882
TOTAL 238445 112464 55644 16247 130256 74411 55844
App Summary
Pss(KB)
------
Java Heap: 66436
Native Heap: 25984
Code: 55644
Stack: 2364
Graphics: 1548
Private Other: 16132
System: 70337
TOTAL: 238445 TOTAL SWAP PSS: 16247
Objects
Views: 3 ViewRootImpl: 1
AppContexts: 20 Activities: 0
Assets: 8 AssetManagers: 6
Local Binders: 888 Proxy Binders: 1652
Parcel memory: 1697 Parcel count: 751
Death Recipients: 753 OpenSSL Sockets: 0
数据来源:
纵轴:
属性名
说明
Native Heap
在 Native Code 中使用 malloc 分配出的内存
Dalvik Heap
Dalvik 虚拟机分配的空间,不包括它自身的开销。Dalvik 堆中和 Zygote 进程共享的部分算是 sharedDirty
Dalvik Other
类数据结构和索引占据的内存
Stack
栈内存
Cursor
CursorWindow 占用的空间,与 SQL 有关
Ashmem
匿名共享内存,此类内存与 cache shrinker 关联,可以控制cache shrinker在适当时机回收这些共享内存
Gfx dev
/dev/kgsl-3d0 占用的内存
Other dev
内部driver占用的内存
.so mmap
映射的 .so(native)代码占用的内存
.jar mmap
Java 文件代码占用内存
.apk mmap
apk 代码占用内存
.ttf mmap
ttf 文件代码占用内存
.dex mmap
映射的 .dex(Dalvik 或 ART)代码占用的内存
.oat mmap
代码映像占用的 RAM 量。此映像在所有应用之间共享,不受特定应用影响
.art mmap
堆映像占用的 RAM 量。此映像在所有应用之间共享,不受特定应用影响。尽管 ART 映像包含 Object 实例,它仍然不会计入您的堆大小
Other mmap
其它文件占用的内存
横轴:
属性名
说明
Pss Total
实际使用的内存,这里考虑了与 Zygote 的共享。任何独占的内存页直接计算它的PSS值,而和其它进程共享的页则按照共享的比例计算PSS值
Private Dirty
进程私有的,相对磁盘数据有改动的内存
Private Clean
进程私有的,相对磁盘数据没有修改的内存
SwapPss Dirty
Android 4.4 的一个优化,swap to zRAM。牺牲CPU,减少内存。这两个值的区别在于内核是否是统计按比例分出的swap数据,是的输出为 SwapPss Dirty。
Swap Dirty
Whether the kernel reports proportional swap usage
Heap相关:
Heap Size
Heap Alloc
Heap Free
Native Heap
从mallinfo usmblks获得,代表最大总共分配空间
从mallinfo uorblks获得,总共已分配空间
从mallinfo fordblks获得,代表总共剩余空间
Dalvik Heap
从Runtime totalMemory()获得,Dalvik Heap总共的内存大小
Runtime totalMemory()-freeMemory() ,Dalvik Heap分配的内存大小
从Runtime freeMemory()获得,Dalvik Heap剩余的内存大小
App Summary:
属性名
内存组成
Java Heap
Dalvik Heap 的 Private Dirty
.art mmap 的 Private Dirty + Private Clean
Native Heap
Native Heap 的 Private Dirty
Code
.so mmap
.jar mmap
.apk mmap
.ttf mmap
.dex mmap
.oat mmap的 Private Dirty + Private Clean
Stack
Stack 的 Private Dirty
Graphics
Gfx dev
EGL mtrack
GL mtrack的 Private Dirty + Private Clean
Private Other
Native Heap
Dalvik Heap
- HEAP_UNKNOWN的 Private Dirty + Private Clean
System
Native Heap
Dalvik Heap
HEAP_UNKNOWN的 Pss + SwapPss Dirty - Private Dirty - Private Clean
TOTAL
Native Heap
Dalvik Heap
HEAP_UNKNOWN的 Pss + SwapPss Dirty
TOTAL SWAP PSS
Native Heap
Dalvik Heap
HEAP_UNKNOWN的 SwapPss Dirty
TOTAL SWAP (KB)
Native HEAP
Dalvik Heap
HEAP_UNKNOWN的 Swap Dirty
Object:
这里通常会通过看Activities、AppContexts来判断是否有内存泄漏,比如刚退出应用,查看Object中Activities是否为0,如果不为0,则有Activity没有销毁,很有可能存在泄漏。