一、内存指标
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/$
从代码看:这部分内容是从/proc/$
那么总结下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没有销毁,很有可能存在泄漏。
参考:
https://www.cnblogs.com/adv-qbj/p/4719787.html
https://www.jianshu.com/p/9e47b1cce077
https://blog.csdn.net/zsj100213/article/details/78572383
https://blog.csdn.net/manok/article/details/82896288