1 内存指标概念

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

2 内存分析命令

常用的内存调优分析命令:

  1. dumpsys meminfo
  2. cat /proc/meminfo
  3. procrank
  4. free
  5. showmap
  6. vmstat

2.1 dumpsys meminfo

Applications Memory Usage (in Kilobytes):
Uptime: 56803948 Realtime: 328348979Total PSS by process://以process来划分,按照进程的PSS的大小来由大到小进行排列172,452K: system (pid 957)168,257K: com.android.systemui (pid 1309 / activities)89,061K: com.emoji.keyboard.touchpal (pid 1304)86,228K: com.facebook.katana (pid 2903)67,818K: surfaceflinger (pid 360)
…………………Total PSS by OOM adjustment://以OOM的类别来进行划分Native/System/Persistent/Foreground/Visible/Perceptible/A Services/Home/B Services/Cached,分别显示每类的进程情况
1,254,482K: Native67,818K: surfaceflinger (pid 360)59,100K: cameraserver (pid 490)30,774K: zygote (pid 487)20,646K: zygote64 (pid 486)
………………172,452K: System172,452K: system (pid 957)420,974K: Persistent168,257K: com.android.systemui (pid 1309 / activities)64,637K: com.android.gallery3d (pid 2062)40,031K: com.mediatek.camera (pid 1591)
………………121,299K: Foreground17,787K: android.process.acore (pid 18435)146,745K: Visible51,754K: com.google.android.gms.persistent (pid 2101)48,103K: com.google.android.gms (pid 2367)
………………89,061K: Perceptible89,061K: com.emoji.keyboard.touchpal (pid 1304)24,594K: A Services
………………31,628K: B Services17,720K: android.process.media (pid 2074)
………………203,989K: Cached86,228K: com.facebook.katana (pid 2903)45,959K: com.google.android.googlequicksearchbox:search (pid 27390)
………………Total PSS by category://以category进行划分,以Dalvik/Native/.art mmap/.dex map等划分的各类进程的总PSS情况598,588K: .dex mmap424,583K: .apk mmap359,730K: .so mmap315,668K: Dalvik
……………………Total RAM: 5,958,380K (status moderate)Free RAM: 3,312,193K (  203,989K cached pss + 1,369,648K cached kernel + 1,320,228K free +   108,784K ion cached +   309,544K gpu cached)Used RAM: 2,635,803K (2,261,235K used pss +   356,284K kernel +        56K trace buffer +    18,228K ion disp +         0K cma usage)Lost RAM:    10,380KZRAM:         4K physical used for         0K in swap (2,979,188K total swap)Tuning: 192 (large 384), oom   322,560K, restore limit   107,520K (high-end-gfx)

重点说的是total部分的内容,搜索网上的资料没有找到特别有用的内容,只能自己查看code,这里面有些值可以透过读取proc/meminfo的节点获取,有些事透过其他的节点获取(在activitymanageservice.java中),透过readMemInfo()和readExtraMemInfo()函数来获取。

Total RAM: 5,958,380K (status moderate)//系统总共的可用内存。
Free RAM: 3,312,193K (  203,989K cached pss + 1,369,648K cached kernel + 1,320,228K free +   108,784K ion cached +   309,544K gpu cached)

//cached pss指上层app cache所占的memory大小。

//cached kernel指kernel cache的大小,他等于总的meminfo中的cache大小+buffer大小减去mapped大小。
//free指unsed memory的大小
//ion cached和gpu cached额外的一些内存cache,透过读取/sys/kernel/debug/shrinker节点获取
以上cache都是可以释放的内存大小。
Used RAM: 2,635,803K (2,261,235K used pss +   356,284K kernel +        56K trace buffer +    18,228K ion disp +         0K cma usage)

//used pss指上层app所占用的不可回收的内存大小,它等于APP所占的总的内存大小减去cached pss的大小。

//kernel值kernel所占的不可回收的内存大小,包括share memory,slab,vmalloc_used,pagetables,kernelstack.
//trace buffer指系统trace所占的空间,读取/sys/kernel/debug/buffer_total_size_kb或取
//ion disp不清楚这个是什么,字面意思貌似和display相关,透过读取/d/ion/clients/文件夹里面节点的size属性
//cma usage这个也不是很清楚,读取的是/sys/kernel/debug/cmainfo节点的属性值
 Lost RAM:    10,380K

这个我理解为kernel申请的由driver使用的内存,kernel不会计算这部分内存,所以称为lost ram。

Android后台cached的APP个数可以透过MAX_CACHED_APPS设定,默认是32个。

2.2 cat /proc/meminfo

MemTotal:        2857.032 kB  //RAM可用的总大小 (即物理总内存减去系统预留和内核二进制代码大小)
MemFree:         1020.708 kB  //RAM未使用的大小
Buffers:           75.104 kB  //用于块设备访问的缓冲
Cached:           448.244 kB  //用于高速缓存
SwapCached:             0 kB  //用于swap缓存Active:           832.900 kB  //活跃使用状态,记录最近使用过的内存,通常不回收用于其它目的
Inactive:         391.128 kB  //非活跃使用状态,记录最近并没有使用过的内存,能够被回收用于其他目的
Active(anon):     700.744 kB  //Active = Active(anon) + Active(file)
Inactive(anon):       228 kB  //Inactive = Inactive(anon) + Inactive(file)
Active(file):     132.156 kB
Inactive(file):   390.900 kBUnevictable:            0 kB
Mlocked:                0 kBSwapTotal:        524.284 kB  //swap总大小
SwapFree:         524.284 kB  //swap可用大小
Dirty:                  0 kB  //等待往磁盘回写的大小
Writeback:              0 kB  //正在往磁盘回写的大小AnonPages:        700.700 kB
Mapped:           187.096 kB  //通过mmap()分配的内存,用于map设备、文件或者库
Shmem:               .312 kBSlab:              91.276 kB  //kernel数据结构的缓存大小,Slab=SReclaimable+SUnreclaim
SReclaimable:      32.484 kB  //可回收的slab的大小
SUnreclaim:        58.792 kB  //不可回收slab的大小KernelStack:       25.024 kB
PageTables:        23.752 kB  //以最低的页表级
NFS_Unstable:           0 kB  //不稳定页表的大小
Bounce:                 0 kB
WritebackTmp:           0 kB
CommitLimit:     1952.800 kB
Committed_AS:   82204.348 kB   //评估完成的工作量,代表最糟糕case下的值,该值也包含swap内存VmallocTotal:  251658.176 kB  //总分配的虚拟地址空间
VmallocUsed:      166.648 kB  //已使用的虚拟地址空间
VmallocChunk:  251398.700 kB  //虚拟地址空间可用的最大连续内存块
MemTotal:指内核在启动过程中除去kernel的init,code,data段的内存,其他module reserve的内存以及init所reserve的内存外所剩余的总内存。
MemFree:系统完全没有使用的内存。
MemAvailable:有些应用程序会根据系统的可用内存大小自动调整内存申请的多少,所以需要一个记录当前可用内存数量的统计值,MemFree并不适用,因为MemFree不能代表全部可用的内存,系统中有些内存虽然已被使用但是可以回收的,比如cache/buffer、slab都有一部分可以回收,所以这部分可回收的内存加上MemFree才是系统可用的内存,即MemAvailable。/proc/meminfo中的MemAvailable是内核使用特定的算法估算出来的,要注意这是一个估计值,并不精确。

内存都到哪里去了?

使用内存的,不是kernel就是用户进程,下面我们就分类讨论。

注:page cache比较特殊,很难区分是属于kernel还是属于进程,其中被进程mmap的页面自然是属于进程的了,而另一些页面没有被mapped到任何进程,那就只能算是属于kernel了。

内核

内核所用内存的静态部分,比如内核代码、页描述符等数据在引导阶段就分配掉了,并不计入MemTotal里,而是算作Reserved(在dmesg中能看到)。而内核所用内存的动态部分,是通过上文提到的几个接口申请的,其中通过alloc_pages申请的内存有可能未纳入统计,就像黑洞一样。

下面讨论的都是/proc/meminfo中所统计的部分。

SLAB:通过slab分配的内存被统计在以下三个值中:
  • SReclaimable: slab中可回收的部分。调用kmem_getpages()时加上SLAB_RECLAIM_ACCOUNT标记,表明是可回收的,计入SReclaimable,否则计入SUnreclaim。
  • SUnreclaim: slab中不可回收的部分。
  • Slab: slab中所有的内存,等于以上两者之和。
VmallocUsed

通过vmalloc分配的内存都统计在/proc/meminfo的 VmallocUsed 值中,但是要注意这个值不止包括了分配的物理内存,还统计了VM_IOREMAP、VM_MAP等操作的值,譬如VM_IOREMAP是把IO地址映射到内核空间、并未消耗物理内存,所以我们要把它们排除在外。从物理内存分配的角度,我们只关心VM_ALLOC操作,这可以从/proc/vmallocinfo中的vmalloc记录看到:

# grep vmalloc /proc/vmallocinfo
...
0xffffc90004702000-0xffffc9000470b000   36864 alloc_large_system_hash+0x171/0x239 pages=8 vmalloc N0=8
0xffffc9000470b000-0xffffc90004710000   20480 agp_add_bridge+0x2aa/0x440 pages=4 vmalloc N0=4
0xffffc90004710000-0xffffc90004731000  135168 raw_init+0x41/0x141 pages=32 vmalloc N0=32
0xffffc90004736000-0xffffc9000473f000   36864 drm_ht_create+0x55/0x80 [drm] pages=8 vmalloc N0=8
0xffffc90004744000-0xffffc90004746000    8192 dm_table_create+0x9e/0x130 [dm_mod] pages=1 vmalloc N0=1
0xffffc90004746000-0xffffc90004748000    8192 dm_table_create+0x9e/0x130 [dm_mod] pages=1 vmalloc N0=1
...

注:/proc/vmallocinfo中能看到vmalloc来自哪个调用者(caller),那是vmalloc()记录下来的,相应的源代码可见:
mm/vmalloc.c: vmalloc > __vmalloc_node_flags > __vmalloc_node > __vmalloc_node_range > __get_vm_area_node > setup_vmalloc_vm


一些driver以及网络模块和文件系统模块可能会调用vmalloc,加载内核模块(kernel module)时也会用到,可参见 kernel/module.c。
kernel modules (内核模块)

系统已经加载的内核模块可以用 lsmod 命令查看,注意第二列就是内核模块所占内存的大小,通过它可以统计内核模块所占用的内存大小,但这并不准,因为”lsmod”列出的是[init_size+core_size],而实际给kernel module分配的内存是以page为单位的,不足 1 page的部分也会得到整个page,此外每个module还会分到一页额外的guard page。

# lsmod | less
Module                  Size  Used by
rpcsec_gss_krb5        31477  0
auth_rpcgss            59343  1 rpcsec_gss_krb5
nfsv4                 474429  0
dns_resolver           13140  1 nfsv4
nfs                   246411  1 nfsv4
lockd                  93977  1 nfs
sunrpc                295293  5 nfs,rpcsec_gss_krb5,auth_rpcgss,lockd,nfsv4
fscache                57813  2 nfs,nfsv4

lsmod的信息来自/proc/modules,它显示的size包括init_size和core_size,相应的源代码参见:

1
2
3
4
5
6
7
8

1
2
3
4
5
6
7
8

// kernel/module.c
static int m_show(struct seq_file *m, void *p)
{
...seq_printf(m, "%s %u",mod->name, mod->init_size + mod->core_size);
...
}

注:我们可以在 /sys/module/<module-name>/ 目录下分别看到coresize和initsize的值。

kernel module的内存是通过vmalloc()分配的(参见下列源代码),所以在/proc/vmallocinfo中会有记录,也就是说我们可以不必通过”lsmod”命令来统计kernel module所占的内存大小,通过/proc/vmallocinfo就行了,而且还比lsmod更准确,为什么这么说呢?

// kernel/module.c
static int move_module(struct module *mod, struct load_info *info)
{
...ptr = module_alloc_update_bounds(mod->core_size);
...if (mod->init_size) {ptr = module_alloc_update_bounds(mod->init_size);
...
}// 注:module_alloc_update_bounds()最终会调用vmalloc_exec()

因为给kernel module分配内存是以page为单位的,不足 1 page的部分也会得到整个page,此外,每个module还会分到一页额外的guard page。
详见:mm/vmalloc.c: __get_vm_area_node()

而”lsmod”列出的是[init_size+core_size],比实际分配给kernel module的内存小。我们做个实验来说明:

# 先卸载floppy模块
$ modprobe -r floppy
# 确认floppy模块已经不在了
$ lsmod | grep floppy
# 记录vmallocinfo以供随后比较
$ cat /proc/vmallocinfo > vmallocinfo.1# 加载floppy模块
$ modprobe -a floppy
# 注意floppy模块的大小是69417字节:
$ lsmod | grep floppy
floppy                 69417  0
$ cat /proc/vmallocinfo > vmallocinfo.2
# 然而,我们看到vmallocinfo中记录的是分配了73728字节:
$ diff vmallocinfo.1 vmallocinfo.2
68a69
> 0xffffffffa03d7000-0xffffffffa03e9000   73728 module_alloc_update_bounds+0x14/0x70 pages=17 vmalloc N0=17# 为什么lsmod看到的内存大小与vmallocinfo不同呢?
# 因为给kernel module分配内存是以page为单位的,而且外加一个guard page
# 我们来验证一下:
$ bc -q
69417%4096
3881    <--- 不能被4096整除
69417/4096
16      <--- 相当于16 pages,加上面的3881字节,会分配17 pages
18*4096 <--- 17 pages 加上 1个guard page
73728   <--- 正好是vmallocinfo记录的大小

所以结论是kernel module所占用的内存包含在/proc/vmallocinfo的统计之中,不必再去计算”lsmod”的结果了,而且”lsmod”也不准。

HardwareCorrupted

当系统检测到内存的硬件故障时,会把有问题的页面删除掉,不再使用,/proc/meminfo中的HardwareCorrupted统计了删除掉的内存页的总大小。相应的代码参见 mm/memory-failure.c: memory_failure()

PageTables

Page Table用于将内存的虚拟地址翻译成物理地址,随着内存地址分配得越来越多,Page Table会增大,/proc/meminfo中的PageTables统计了Page Table所占用的内存大小。

注:请把Page Table与Page Frame(页帧)区分开,物理内存的最小单位是page frame,每个物理页对应一个描述符(struct page),在内核的引导阶段就会分配好、保存在mem_map[]数组中,mem_map[]所占用的内存被统计在dmesg显示的reserved中,/proc/meminfo的MemTotal是不包含它们的。(在NUMA系统上可能会有多个mem_map数组,在node_data中或mem_section中)。
而Page Table的用途是翻译虚拟地址和物理地址,它是会动态变化的,要从MemTotal中消耗内存。

KernelStack

每一个用户线程都会分配一个kernel stack(内核栈),内核栈虽然属于线程,但用户态的代码不能访问,只有通过系统调用(syscall)、自陷(trap)或异常(exception)进入内核态的时候才会用到,也就是说内核栈是给kernel code使用的。在x86系统上Linux的内核栈大小是固定的8K或16K。

Kernel stack(内核栈)是常驻内存的,既不包括在LRU lists里,也不包括在进程的RSS/PSS内存里,所以我们认为它是kernel消耗的内存。统计值是/proc/meminfo的KernelStack。

Buffers

Buffers统计的是直接访问块设备时的缓冲区的总大小,有时候对文件系统元数据的操作也会用到buffers。这部分内存不好直接对应到某个用户进程,应该算作kernel占用。

Bounce

有些老设备只能访问低端内存,比如16M以下的内存,当应用程序发出一个I/O 请求,DMA的目的地址却是高端内存时(比如在16M以上),内核将在低端内存中分配一个临时buffer作为跳转,把位于高端内存的缓存数据复制到此处。这种额外的数据拷贝被称为“bounce buffering”,会降低I/O 性能。大量分配的bounce buffers 也会占用额外的内存。

用户进程

/proc/meminfo统计的是系统全局的内存使用状况,单个进程的情况要看/proc/<pid>/下的smaps等等。

Shmem

/proc/meminfo中的Shmem统计的内容包括:

  • shared memory
  • tmpfs。
AnonPages

用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页)。Anonymous pages(匿名页)的数量统计在/proc/meminfo的AnonPages中。

以下是几个事实,有助于了解Anonymous Pages:

  • 所有page cache里的页面(Cached)都是file-backed pages,不是Anonymous Pages。
    注:shared memory 不属于 AnonPages,而是属于Cached,因为shared memory基于tmpfs,所以被视为file-backed、在page cache里,上一节解释过。
  • mmap private anonymous pages属于AnonPages(Anonymous Pages),而mmap shared anonymous pages属于Cached(file-backed pages),因为shared anonymous mmap也是基于tmpfs的
  • Anonymous Pages是与用户进程共存的,一旦进程退出,则Anonymous pages也释放,不像page cache即使文件与进程不关联了还可以缓存。
  • AnonPages统计值中包含了Transparent HugePages (THP)对应的 AnonHugePages
Mapped

上面提到的用户进程的file-backed pages就对应着/proc/meminfo中的”Mapped”。Page cache中(“Cached”)包含了文件的缓存页,其中有些文件当前已不在使用,page cache仍然可能保留着它们的缓存页面;而另一些文件正被用户进程关联,比如shared libraries、可执行程序的文件、mmap的文件等,这些文件的缓存页就称为mapped。

/proc/meminfo中的”Mapped”就统计了page cache(“Cached”)中所有的mapped页面。

因为Linux系统上shared memory & tmpfs被计入page cache(“Cached”),所以被attached的shared memory、以及tmpfs上被map的文件都算做”Mapped”。

进程所占的内存页分为anonymous pages和file-backed pages,理论上应该有:
【所有进程的PSS之和】 == 【Mapped + AnonPages】。
然而实际测试的结果,虽然两者很接近,却总是无法精确相等,我猜也许是因为进程始终在变化、采集的/proc/[1-9]*/smaps以及/proc/meminfo其实不是来自同一个时间点的缘故。

Cached

Page Cache里包括所有file-backed pages,统计在/proc/meminfo的”Cached”中。

  • Cached不仅包括mapped,也包括unmapped的页面,当一个文件不再与进程关联之后,原来在page cache中的页面并不会立即回收,仍然被计入Cached,还留在LRU中,但是 Mapped 统计值会减小。【ummaped = (Cached – Mapped)】
  • Cached包含tmpfs中的文件,POSIX/SysV shared memory,以及shared anonymous mmap。
    注:POSIX/SysV shared memory和shared anonymous mmap在内核中都是基于tmpfs实现的

有意思的是,Shared memory和tmpfs在不发生swap-out的时候属于”Cached”,而在swap-out/swap-in的过程中会被加进swap cache中、属于”SwapCached”,一旦进了”SwapCached”,就不再属于”Cached”了。”Cached”和”SwapCached”两个统计值是互不重叠的

SwapCached

我们说过,匿名页(anonymous pages)要用到交换区,而shared memory和tmpfs虽然未统计在AnonPages里,但它们背后没有硬盘文件,所以也是需要交换区的。也就是说需要用到交换区的内存包括:”AnonPages”和”Shmem”,我们姑且把它们统称为匿名页好了。

交换区可以包括一个或多个交换区设备(裸盘、逻辑卷、文件都可以充当交换区设备),每一个交换区设备都对应自己的swap cache,可以把swap cache理解为交换区设备的”page cache”:page cache对应的是一个个文件,swap cache对应的是一个个交换区设备,kernel管理swap cache与管理page cache一样,用的都是radix-tree,唯一的区别是:page cache与文件的对应关系在打开文件时就确定了,而一个匿名页只有在即将被swap-out的时候才决定它会被放到哪一个交换区设备,即匿名页与swap cache的对应关系在即将被swap-out时才确立。

并不是每一个匿名页都在swap cache中,只有以下情形之一的匿名页才在:

  • 匿名页即将被swap-out时会先被放进swap cache,但通常只存在很短暂的时间,因为紧接着在pageout完成之后它就会从swap cache中删除,毕竟swap-out的目的就是为了腾出空闲内存;
    【注:参见mm/vmscan.c: shrink_page_list(),它调用的add_to_swap()会把swap cache页面标记成dirty,然后它调用try_to_unmap()将页面对应的page table mapping都删除,再调用pageout()回写dirty page,最后try_to_free_swap()会把该页从swap cache中删除。】
  • 曾经被swap-out现在又被swap-in的匿名页会在swap cache中,直到页面中的内容发生变化、或者原来用过的交换区空间被回收为止。
    【注:当匿名页的内容发生变化时会删除对应的swap cache,代码参见mm/swapfile.c: reuse_swap_page()。】

/proc/meminfo中的SwapCached背后的含义是:系统中有多少匿名页曾经被swap-out、现在又被swap-in并且swap-in之后页面中的内容一直没发生变化。也就是说,如果这些匿名页需要被swap-out的话,是无需进行I/O write操作的。

“SwapCached”不属于”Cached”,两者没有交叉。

“SwapCached”内存同时也在LRU中,还在”AnonPages”或”Shmem”中,它本身并不占用额外的内存。
Mlocked

“Mlocked”统计的是被mlock()系统调用锁定的内存大小。被锁定的内存因为不能pageout/swapout,会从Active/Inactive LRU list移到Unevictable LRU list上。也就是说,当”Mlocked”增加时,”Unevictable”也同步增加,而”Active”或”Inactive”同时减小;当”Mlocked”减小的时候,”Unevictable”也同步减小,而”Active”或”Inactive”同时增加。

“Mlocked”并不是独立的内存空间,它与以下统计项重叠:LRU Unevictable,AnonPages,Shmem,Mapped等。

内存黑洞

追踪Linux系统的内存使用一直是个难题,很多人试着把能想到的各种内存消耗都加在一起,kernel text、kernel modules、buffer、cache、slab、page table、process RSS…等等,却总是与物理内存的大小对不上,这是为什么呢?因为Linux kernel并没有滴水不漏地统计所有的内存分配,kernel动态分配的内存中就有一部分没有计入/proc/meminfo中。

我们知道,Kernel的动态内存分配通过以下几种接口:

  • alloc_pages/__get_free_page: 以页为单位分配
  • vmalloc: 以字节为单位分配虚拟地址连续的内存块
  • slab allocator
    • kmalloc: 以字节为单位分配物理地址连续的内存块,它是以slab为基础的,使用slab层的general caches — 大小为2^n,名称是kmalloc-32、kmalloc-64等(在老kernel上的名称是size-32、size-64等)。

通过slab层分配的内存会被精确统计,可以参见/proc/meminfo中的slab/SReclaimable/SUnreclaim;

通过vmalloc分配的内存也有统计,参见/proc/meminfo中的VmallocUsed 和 /proc/vmallocinfo(下节中还有详述);

而通过alloc_pages分配的内存不会自动统计,除非调用alloc_pages的内核模块或驱动程序主动进行统计,否则我们只能看到free memory减少了,但从/proc/meminfo中看不出它们具体用到哪里去了。比如在VMware guest上有一个常见问题,就是VMWare ESX宿主机会通过guest上的Balloon driver(vmware_balloon module)占用guest的内存,有时占用得太多会导致guest无内存可用,这时去检查guest的/proc/meminfo只看见MemFree很少、但看不出内存的去向,原因就是Balloon driver通过alloc_pages分配内存,没有在/proc/meminfo中留下统计值,所以很难追踪。

kernel内存的统计方式应该比较明确,即

【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】

用户进程的内存主要有三种统计口径:
  • [1]围绕LRU进行统计
    【(Active + Inactive + Unevictable) + (HugePages_Total * Hugepagesize)】
  • [2]围绕Page Cache进行统计
    当SwapCached为0的时候,用户进程的内存总计如下:
    【(Cached + AnonPages) + (HugePages_Total * Hugepagesize)】
    当SwapCached不为0的时候,以上公式不成立,因为SwapCached可能会含有Shmem,而Shmem本来被含在Cached中,一旦swap-out就从Cached转移到了SwapCached,可是我们又不能把SwapCached加进上述公式中,因为SwapCached虽然不与Cached重叠却与AnonPages有重叠,它既可能含有Shared memory又可能含有Anonymous Pages。
  • [3]围绕RSS/PSS进行统计
    把/proc/[1-9]*/smaps 中的 Pss 累加起来就是所有用户进程占用的内存,但是还没有包括Page Cache中unmapped部分、以及HugePages,所以公式如下:
    ΣPss + (Cached – mapped) + (HugePages_Total * Hugepagesize)
所以系统内存的使用情况可以用以下公式表示:
  • MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【Active + Inactive + Unevictable + (HugePages_Total * Hugepagesize)】
  • MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【Cached + AnonPages + (HugePages_Total * Hugepagesize)】
  • MemTotal = MemFree +【Slab+ VmallocUsed + PageTables + KernelStack + Buffers + HardwareCorrupted + Bounce + X】+【ΣPss + (Cached – mapped) + (HugePages_Total * Hugepagesize)】

2.3 procrank

Android procrank  (/system/xbin/procrank) 工具,能够列出进程所占用的内存使用情况,顺序为从高到低。

语法:
Usage: procrank [ -W ] [ -v | -r | -p | -u | -h ]
02.    -v  Sort by VSS.
03.    -r  Sort by RSS.
04.    -p  Sort by PSS.
05.    -u  Sort by USS.
06.        (Default sort order is PSS.)
07.    -R  Reverse sort order (default is descending).
08.    -w  Display statistics for working set only.
09.    -W  Reset working set of all processes.
10.    -h  Display this help screen.  

执行结果

1|root@android:/ # procrank
02.procrank
03.  PID      Vss      Rss      Pss      Uss  cmdline
04. 1780   48188K   36892K   21177K   18196K  system_server
05. 1953   44944K   36056K   17201K   14904K  com.android.systemui
06. 2109   51300K   34888K   15291K   12840K  com.android.launcher
07. 2248   32996K   32948K   14184K   12516K  com.tencent.qqpimsecure
08. 2913   29880K   29796K   11980K    9612K  android.process.acore
09. 1396   36280K   36168K   11762K    7552K  zygote
10. 2058   27200K   27132K   10204K    9120K  com.android.phone
11. 2352   26028K   25960K    9680K    8740K  com.wandoujia.phoenix2
12. 2670   25388K   25312K    8064K    6332K  com.sina.weibo
13. 2029   22276K   22204K    6700K    6040K  android.process.media
14. 2866   23504K   23428K    6161K    4424K  com.sina.weibo.servant
15. 2632   21700K   21624K    6071K    5384K  com.infinit.wostore.ui
16. 1398    8904K    8904K    5948K    5012K  /system/bin/mediaserver
17. 2484   21580K   21508K    5679K    4892K  com.android.email
18. 2704   20512K   20436K    5601K    4936K  com.tmall.wireless:core
19. 2399   20384K   20300K    4990K    4248K  com.android.contacts
20. 2746   20720K   20660K    4973K    4188K  com.android.browser
21. 2090   20376K   20320K    4805K    4084K  com.android.nfc
22. 2043   20516K   20448K    4753K    4012K  com.android.inputmethod.pinyin
23. 2200   20272K   20196K    4679K    3868K  com.android.settings
24. 2563   20028K   19952K    4492K    3640K  com.android.mms
25. 2788   19304K   19232K    4123K    3396K  com.android.calendar
26. 2436   19812K   19744K    4119K    3520K  com.android.providers.calendar
27. 2505   18724K   18648K    3973K    3460K  com.android.exchange
28. 2655   19588K   19516K    3972K    3320K  com.android.dazhihui
29. 2465   19816K   19736K    3894K    3160K  com.android.deskclock
30. 2233   18756K   18684K    3467K    2824K  com.android.music
31. 2605   17540K   17460K    2824K    2264K  com.android.SystemLog
32. 2157   17376K   17296K    2683K    2088K  com.android.location.fused
33. 2193   17156K   17076K    2663K    2124K  com.android.smspush
34. 2590   17364K   17280K    2662K    2092K  com.broadcom.phone.register
35. 2943   17112K   17036K    2653K    2056K  com.android.musicfx
36. 2619   17132K   17056K    2620K    2040K  com.android.voicedialer
37. 2073   17020K   16940K    2603K    1980K  com.broadcom.bt.app.fm
38. 2421   17132K   17056K    2530K    1976K  com.broadcom.app.autoupdate
39. 1395   33216K    3888K    1859K    1520K  /system/bin/surfaceflinger
40. 1394    6544K    3472K    1737K    1468K  /system/bin/rild
41. 1403    3216K    3212K    1673K    1524K  /system/bin/glgps
42. 2962    1660K    1660K    1413K    1408K  procrank
43. 1397    3200K    3200K    1133K     788K  /system/bin/drmserver
44. 2148    1604K    1604K     812K     792K  /system/bin/wpa_supplicant
45. 1392    1312K    1312K     514K     448K  /system/bin/netd
46. 1390    1176K    1176K     418K     360K  /system/bin/vold
47. 1400     892K     892K     295K     280K  /system/bin/keystore
48. 1450     532K     532K     258K     252K  /system/bin/sdcard
49. 1449     228K     228K     208K     208K  /sbin/adbd
50. 1408    3572K     500K     199K     172K  /system/bin/bkmgrd
51.    1     264K     264K     190K     144K  /init
52. 1401     536K     536K     176K     164K  /system/bin/akmdfs
53. 1405     444K     444K     169K     160K  /system/bin/usb_portd
54. 2905     480K     480K     160K     120K  /system/bin/sh
55. 1399     440K     440K     154K     148K  /system/bin/installd
56. 2387     392K     392K     149K     144K  uuids_sys
57. 1437     464K     464K     148K     108K  /system/bin/sh
58. 1393     424K     424K     143K     136K  /system/bin/debuggerd
59. 1023     184K     184K     130K      84K  /sbin/ueventd
60. 2287     440K     440K     124K      88K  sh
61. 2290     440K     440K     124K      88K  sh
62. 1389     336K     336K     109K     104K  /system/bin/servicemanager
63. 1406     360K     360K     106K     100K  /system/bin/atxd_proxy
64. 2289     340K     340K     105K     100K  /data/data/com.tencent.qqpimsecure/files/athena_v2.dat
65.                          ------   ------  ------
66.                         241715K  201748K  TOTAL
67.
68.RAM: 475520K total, 10412K free, 8104K buffers, 190720K cached, 244K shmem, 27252K slab

2.4 free

free命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区。 
语法:
free(选项)
-b:以Byte为单位显示内存使用情况;
-k:以KB为单位显示内存使用情况;
-m:以MB为单位显示内存使用情况;
-o:不显示缓冲区调节列;
-s<间隔秒数>:持续观察内存使用状况;
-t:显示内存总和列;
-V:显示版本信息。

执行结果:

free -mtotal       used       free     shared    buffers     cached
Mem:          2016       1973         42          0        163       1497
-/+ buffers/cache:        312       1703
Swap:         4094          0       4094

第一部分Mem行解释:

total:内存总数;
used:已经使用的内存数;
free:空闲的内存数;
shared:当前已经废弃不用;
buffers Buffer:缓存内存数;
cached Page:缓存内存数。

关系:total = used + free

第二部分(-/+ buffers/cache)解释:
(-buffers/cache) used内存数:第一部分Mem行中的 used – buffers – cached
(+buffers/cache) free内存数: 第一部分Mem行中的 free + buffers + cached

可见-buffers/cache反映的是被程序实实在在吃掉的内存,而+buffers/cache反映的是可以挪用的内存总数。

第三部分是指交换分区。

2.5 showmap

用于查看虚拟地址区域的内存情况。

语法:

showmap -a [pid]
phone:/ # showmap -a 10901start    end      virtual                   shared   shared  private  privateaddr     addr     size      RSS      PSS    clean    dirty    clean    dirty object
-------- -------- -------- -------- -------- -------- -------- -------- -------- ------------------------------
f3b87000 f3d85000     2040        4        4        0        0        4        0 /dev/binder
  • start addr和end addr:分别代表进程空间的起止虚拟地址;
  • virtual size/ RSS /PSS这些前面介绍过;
  • shared clean:代表多个进程的虚拟地址可指向这块物理空间,即有多少个进程共享这个库;
  • shared: 共享数据
  • private: 该进程私有数据
  • clean: 干净数据,是指该内存数据与disk数据一致,当内存紧张时,可直接释放内存,不需要回写到disk
  • dirty: 脏数据,与disk数据不一致,需要先回写到disk,才能被释放。
  • Object指的是进程里有多少个对象(库),后面是对象或库的名称。

其中Dirty页面如果没有交换机制的情况下,应该是不能回收的。

功能与cat /proc/[pid]/maps基本一致。

2.6 vmstat

vmstat命令的含义为显示虚拟内存状态(“Viryual Memor Statics”),但是它可以报告关于进程、内存、I/O等系统整体运行状态。

语法:
Usage: vmstat [ -n iterations ] [ -d delay ] [ -r header_repeat ]-n iterations     数据循环输出的次数-d delay          两次数据间的延迟时长(单位:S)-r header_repeat  循环多少次,再输出一次头信息行
执行结果:
vmstat
procs  memory                       system          cpur  b   free  mapped   anon   slab    in   cs  flt  us ni sy id wa ir2  0  663436 232836 915192 113960   196  274    0   8  0  2 99  0  00  0  663444 232836 915108 113960   180  260    0   7  0  3 99  0  00  0  663476 232836 915216 113960   154  224    0   2  0  5 99  0  01  0  663132 232836 915304 113960   179  259    0  11  0  3 99  0  02  0  663124 232836 915096 113960   110  175    0   4  0  3 99  0  0

参数列总共15个参数,分为4大类:

Procs(进程)

  • r: 运行队列中进程数量,这个值也可以判断是否需要增加CPU。(长期大于1)
  • b: 等待IO的进程数量。

Memory(内存)

  • free: 可用内存大小
  • mapped:mmap映射的内存大小
  • anon: 匿名内存大小
  • slab: slab的内存大小

system(系统)

  • in: 每秒中断数,包括时钟中断。
  • cs: 每秒上下文切换数。

注意:上面2个值越大,会看到由内核消耗的CPU时间会越大。

CPU(以百分比表示)

  • us: 用户进程执行时间百分比(user time)

us的值比较高时,说明用户进程消耗的CPU时间多,但是如果长期超50%的使用,那么我们就该考虑优化程序算法或者进行加速。

  • sy: 内核系统进程执行时间百分比(system time)

sy的值高时,说明系统内核消耗的CPU资源多,这并不是良性表现,我们应该检查原因。

  • wa: IO等待时间百分比

wa的值高时,说明IO等待比较严重,这可能由于磁盘大量作随机访问造成,也有可能磁盘出现瓶颈(块操作)。

  • id: 空闲时间百分比
  • ni: nice time
  • ir: interrupt time

小结

  1. dumpsys meminfo适用场景: 查看进程的oom adj,或者dalvik/native等区域内存情况,或者某个进程或apk的内存情况,功能非常强大;
  2. procrank适用场景: 查看进程的VSS/RSS/PSS/USS各个内存指标;
  3. cat /proc/meminfo适用场景: 查看系统的详尽内存信息,包含内核情况;
  4. free适用场景: 只查看系统的可用内存;
  5. showmap适用场景: 查看进程的虚拟地址空间的内存分配情况;
  6. vmstat适用场景: 周期性地打印出进程运行队列、系统切换、CPU时间占比等情况;

android内存分析命令相关推荐

  1. android内存分析命令,Android内存问题分析一些命令

    1. 查看当前手机HEAP size 设定 adb shell getprop | grep heap [dalvik.vm.heapgrowthlimit]: [192m] [dalvik.vm.h ...

  2. Android内存分析和调优(上)

    Android内存分析和调优(上) Android内存分析和调优(上) Android内存分析工具(四):adb命令 posted on 2017-09-25 19:29 时空观察者9号 阅读(... ...

  3. Android内存分析

    最近简单学些了android内存分析,下面为一些学习笔记和整理. 一.内存数据的获取 1. 查看手机系统内存信息 adb pull /system/build.prop 打开build.prop可以查 ...

  4. Android内存分析和调优

    最近我们的android app占用了大量内存,于是领导安排做减少内存占用的工作. 要优化内存,首先要做的就是分析内存占用情况.android提供了多个工具和命令进行内存分析. 第一层 Procran ...

  5. android 内存分析 名词解释,Android内存分析和调优(下)

    出自:http://www.cnblogs.com/zdwillie/p/3287150.html 最后一部分是关于native heap,.dex,/dev/other的优化.​ Native He ...

  6. Android 内存分析总结

    一直没有写博客的习惯,最近觉得年纪貌似有点大了,不像以前记忆这么好,想找个方式梳理一下知识,刚好最近在ITeye上看之前一起工作过的一个大哥写的一些关于状态机的一些东西,就萌生了也写一写,记录一下的想 ...

  7. Android内存分析工具:Memory Profiler

    一.前言  我们知道,Android系统检测到app有不再使用对象时,就会进行内存回收相关的工作. 尽管Android检测无用对象.回收内存的方法在不断改进,  但在目前所有的Android版本中,进 ...

  8. Android内存分析工具集【8】-TraceView

    常用的性能检测工具是traceview,集成于 Android Device Monitor 中.从Android Studio3.0开始, Android Device Monitor 被废弃,取而 ...

  9. 图解Android 内存分析工具之Mat使用教程

    2019独角兽企业重金招聘Python工程师标准>>>                 感觉程序员都不太喜欢文字多的阅读,所以用图表达更简单易懂. 1.  安装 http://dow ...

最新文章

  1. mysql中比较函数_MySQL:MySQL层比较函数调用
  2. 【每日一题】502. IPO
  3. jQuery选择器总结(上)
  4. 034_webpack中的加载器
  5. Python语言学习之字母L开头函数使用集锦:logging日志用法之详细攻略
  6. JavaScript实现RadixSort基数排序算法(附完整源码)
  7. window下eclipse +cdt+cygwin做C,C++开发环境搭建 (转自:http://blog.csdn.net/thinkandchange/article/details/7935)
  8. php.ini-dist和php.ini区别,php.ini-dist 和 php.ini-recommended 的区别介绍(方便开发与安全的朋友)...
  9. nginx访问日志常用变量
  10. Javascript Array对象 sort()方法,记忆方法,方法扩展
  11. 面试:谈谈你对jQuery的理解
  12. java yml_Spring Boot使用yml格式进行配置的方法
  13. 未来已来,如何成为一名人工智能产品经理
  14. 鸿蒙智慧屏安装apk,亲测华为智慧屏支持安装以下第三方软件,大家赶紧试试!...
  15. 小米(xiaomi)红米(Redmi)手机一开机就自动重启:find device closed unexpectedly
  16. HTML网页的基本结构
  17. Kali自带密码字典rockyou.txt解压
  18. 关于PX像素、PT点数、CM厘米、MM毫米之间的换算
  19. IOS UIKit基础控件的使用
  20. 商业研究(13):下厨房,从投资角度看这个项目的前景和价值

热门文章

  1. input正则邮箱_用正则表达式匹配邮箱地址
  2. photoshop修改启动图教程
  3. SQL查询列出每个班的班号和总人数
  4. unity cardboard 导出
  5. 深入理解L0,L1和L2正则化
  6. 基于STM32F1实现秒表及万年历功能【寄存器版】
  7. 超级好看又易上手教你用python画樱花
  8. 危化园区信息化管理平台(附方案+源码)
  9. Android中图片的裁剪与压缩
  10. PLB: Congestion Signals are Simple and Effective for Network Load Balancing读后思考