/proc/meminfo
/prox/meminfo
是Linux
系统统计内存状态非常重要的接口,上层的free
亦或者Android
系统的lmk
读取内存信息都来源于这个接口,其实现也非常简单,就是将内核中记录的各种内核数据打印出来,内存信息也非常全!
如下是kernel-5.10
版本输出的信息:
MemTotal: 7334508 kB
MemFree: 1327068 kB
MemAvailable: 3464796 kB
Buffers: 2016 kB
Cached: 1888764 kB
SwapCached: 5848 kB
Active: 776884 kB
Inactive: 1769432 kB
Active(anon): 406908 kB
Inactive(anon): 394548 kB
Active(file): 369976 kB
Inactive(file): 1374884 kB
Unevictable: 128488 kB
Mlocked: 127256 kB
SwapTotal: 6291452 kB
SwapFree: 4613628 kB
Dirty: 740 kB
Writeback: 0 kB
AnonPages: 779480 kB
Mapped: 584580 kB
Shmem: 20476 kB
KReclaimable: 782960 kB
Slab: 504928 kB
SReclaimable: 128860 kB
SUnreclaim: 376068 kB
KernelStack: 74848 kB
ShadowCallStack: 18792 kB
PageTables: 129676 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 9958704 kB
Committed_AS: 141040480 kB
VmallocTotal: 262930368 kB
VmallocUsed: 193260 kB
VmallocChunk: 0 kB
Percpu: 10336 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 4096 kB
FilePmdMapped: 4096 kB
CmaTotal: 483328 kB
CmaFree: 139088 kB
MemTotal
上面提到,
/proc/meminfo
输出的是内核记录的内存信息,其和实际的物理内存信息是有差异的,MemTotal
指的是内存管理的总物理内存,比如我这个设备配置的是8G
的内存,但是其输出的总内存只有7334508,这是因为本身OS
进行内存管理就需要占用到一定的内存,可参考memblock
内存管理模块。内核通过_totalram_pages
原子变量记录。
MemFree
系统中可以立即被使用的内存。
MemAvailable
系统可用内存,跟
MemFree
是有区分的,因为内核的一些缓存以及swap
机制,部分内存可以通过”丢弃“、回写或者压缩,交换等动作而回收,然而,精确的判断系统可用内存是非常困难的,也并不需要精确判断,因为系统内存一直都是一个动态调整的过程,所以Linux
内核采用一种估算的方法,估算的算法也非常简单,如下是kernel-5.10
估算系统可用内存的方法:long si_mem_available(void) { long available; unsigned long pagecache; unsigned long wmark_low = 0; unsigned long pages[NR_LRU_LISTS]; unsigned long reclaimable; struct zone *zone; int lru; for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) pages[lru] = global_node_page_state(NR_LRU_BASE + lru); for_each_zone(zone) wmark_low += low_wmark_pages(zone); /* * Estimate the amount of memory available for userspace allocations, * without causing swapping. */ available = global_zone_page_state(NR_FREE_PAGES) - totalreserve_pages; /* * Not all the page cache can be freed, otherwise the system will * start swapping. Assume at least half of the page cache, or the * low watermark worth of cache, needs to stay. */ pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE]; pagecache -= min(pagecache / 2, wmark_low); available += pagecache; /* * Part of the reclaimable slab and other kernel memory consists of * items that are in use, and cannot be freed. Cap this estimate at the * low watermark. */ reclaimable = global_node_page_state_pages(NR_SLAB_RECLAIMABLE_B) + global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE); available += reclaimable - min(reclaimable / 2, wmark_low); if (available < 0) available = 0; return available; }
也就是:
MemAvailable
=MemFree
-Reserve
+pagecache
-min(pagecache / 2, wmark_low)
+reclaimable
-min(reclaimable / 2, wmark_low)
其中:
pagecache
为File LRU
的总大小,reclaimable
则是slab
以及KReclaimable
可以看出来,内核中认为可用内存,并没有将
Anon
内存可以被压缩到Zram中的部分纳入考虑
Buffers
`Cached\
Swapcached`这三个都是和文件页相关的缓存,
Cached
的计算公式也非常简单:cached = global_node_page_state(NR_FILE_PAGES) - total_swapcache_pages() - i.bufferram;
其中,
global_node_page_state(NR_FILE_PAGES)
即为系统文件页的总大小,分三部分:
Cached
:普通文件的page cached
Buffers
:块设备的page cached
Swapcached
:这个是为了内存中的文件页与磁盘中的文件读入\读出设置的一个内存区域
Active
`Inactive\
Active(anon)\
Inactive(anon)\
Active(file)\
Inactive(file)\
Unevictable`这几个都是和内核中的
LRU
链表有关,是对应各个链表的大小
SwapTotal
`SwapFree`
swap
相关的内存大小,现在比较流行的就是使用Zram
去充当Swap
,Zram
是通过压缩算法将内存压缩,然后依旧保存在主存中,只会设计cpu
计算,不会设计IO
交换
Shmem
val->sharedram = global_node_page_state(NR_SHMEM); NR_SHMEM, /* shmem pages (included tmpfs/GEM pages) */
包含共享内存以及
tmpfs
文件系统占用的内存
Slab
`SReclaimable\
SUnreclaim`这个是统计
slab
占用的内存,slab
相关的内存释放可以查看shrink_slab()
为什么slab相关的page不统计到LRU中, 猜测是slab管理的内存较小,以slab object为单位,而LRU是以page为单位管理的
KReclaimable
KReclaimable
就是在SReclaimable
的基础上加上MISC_RECLAIMABLE
,比如Android
中”free“
归还给ion page pool
的内存。show_val_kb(m, "KReclaimable: ", sreclaimable + global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE));
VmallocTotal
`VmallocUsed`
VmallocTotal
其实就是内核空间中Vmalloc
区域的大小,范围是VMALLOC_END - VMALLOC_START
VmallocUsed
是内核通过vmalloc
申请的内存总大小,部分操作仅仅是申请或者是映射,并未分配具体的物理内存,所以该数值不能代表物理内存大小消耗
PageTables
内核保存页表(PTE)所耗费的内存
CmaTotal
`CmaFree`Inactive/active(file)
为什么不等于Cache
Cache
的计算公式是系统所有File
类型的页面减去SwapCached
以及Buffers
,部分File
类型的page
会被加入到Mlock
以及Unevictable
Inactive/active(anon)
为什么不等于AnonPages
这个原因类似于
File
类型page
,但是并不是说Inactive(anon)+active(anon)
;因为 Shmem
会被记录到anon LRU
当中
Inactive/active(file)
为什么不等于Mapped
对于文件页,分为
Mapped
以及Unmapped
,所谓的Mapped
就是被进程所映射的文件页,也就是关联到一个或者多个进程的文件页,Unmapped
即为无关联进程的文件页,有点类似于引用计数的flag
,被Unmapped
的文件页虽然无关联的进程,但是不会立马被回收,因为回收的动作触发并不是针对某一个页面而言,而是继续驻留在LRU
中,可能会被置换到Inactive
,然后被回收
Inactive/active(anon/file)
+ Unevictable
能代表整机已经使用的内存吗?什么样的page
会挂载到LRU
上?操作系统有多少组LRU
这原本是三个疑问,但是都是彼此关联的,合到一起分析;当系统完成完成初始化之后,
BuddySystem
会负责管理系统内存管理,所以LRU
相关的页面都是通过BuddySystem
申请的,而__alloc_page
是BuddySystem
开放的底层接口,通过这个接口衍生出很多内存的申请:
malloc
:用户空间堆内存申请文件页的相关操作:
open
/mmap
vmalloc
:内核空间VMALLOC
段内存申请
ION
/GPU
内存:这个主要看自定义的申请接口,可以看到这些“第三方”的内存申请其实是在BuddySystem
基础上再做了一层内存管理
Slab
:内核中避免内部碎片的内存管理模块这里,只有一个
(1)/(2)
会挂载到LRU
,这两个中内存的申请都有一个特点,使用过程可能会发生缺页异常(Page Fault)
,而挂载LRU
就是在处理缺页异常的过程中实现的,所以LRU
相关的内存并不能代表整个系统的内存使用,可以参考__pagevec_lru_add_fn
的调用过程,这是加入LRU
的唯一入口操作系统有多少组
LRU
,这个跟MEMCG
相关:
- 对于未使能
MEMCG
,一个Node
会有一组LRU
,即lruvec
- 对于使能
MEMCG
,每一组memcg
都会有一组LRU
BuddySystem
?系统所有的内存都交由BuddySystem
管理吗?
BuddySystem
管理的内存单位是一个Zone
,即每一个Zone
都会有一个BuddySystem
# cat /proc/buddyinfo Node 0, zone DMA32 9 17 126 71 157 560 317 34 8 25 0 Node 0, zone Normal 106 3411 514 189 62 18 19 9 2 1 0
并非所有的内存都交由
BuddySystem
管理,因为在bootTime
,BuddySystem
未建立之前,也需要内存管理,从内核打印的信息可以看BuddySystem
管理的内存总数:pr_info("Memory: %luK/%luK available (%luK kernel code, %luK rwdata, %luK rodata, %luK init, %luK bss, %luK reserved, %luK cma-reserved" #ifdef CONFIG_HIGHMEM ", %luK highmem" #endif "%s%s)\n", nr_free_pages() << (PAGE_SHIFT - 10), physpages << (PAGE_SHIFT - 10), codesize >> 10, datasize >> 10, rosize >> 10, (init_data_size + init_code_size) >> 10, bss_size >> 10, (physpages - totalram_pages() - totalcma_pages) << (PAGE_SHIFT - 10), totalcma_pages << (PAGE_SHIFT - 10), #ifdef CONFIG_HIGHMEM totalhigh_pages() << (PAGE_SHIFT - 10), #endif str ? ", " : "", str ? str : "");
adb shell dmesg | grep "Memory:" [ 0.000000] Memory: 6796336K/8131584K available (28800K kernel code, 2082K rwdata, 10608K rodata, 3008K init, 975K bss, 851920K reserved, 483328K cma-reserved)
其中
6796336K
即为BuddySystem
管理的所有内存???这个是一个BuddySystem
初始化完成之后,系统剩余的内存
File
和Anon
吗free
c@M:~$ adb shell free -h
total used free shared buffers
Mem: 6.9G 6.6G 365M 4.3M 11M
-/+ buffers/cache: 6.6G 376M
Swap: 6.0G 234M 5.7G
free
命令比较简单,仅列出当前系统总内存,已使用内存、剩余内存以及合并缓存之后系统内存情况
dumpsys mmeinfo -S
dumpsys
是Android
系统提供的工具,可以查看系统的各种信息,其中dump meminfo -S
可以看到系统每一个进程的内存使用情况(包含RSS\PSS\Swap
),还可以通过分优先级查看,同时还能查看当前系统总内存情况:
.....
Total RAM: 7,334,496K (status normal)
Free RAM: 4,300,869K ( 327,225K cached pss + 3,435,420K cached kernel + 538,224K free)
DMA-BUF: 586,304K ( 44,928K mapped + 541,376K unmapped)
DMA-BUF Heaps: 586,304K
DMA-BUF Heaps pool: 292,736K
GPU: 697,736K
Used RAM: 3,639,797K (2,393,417K used pss + 1,246,380K kernel)
Lost RAM: 790,816K
ZRAM: 338,440K physical used for 1,165,540K in swap (6,291,452K total swap)
Tuning: 256 (large 512), oom 322,560K, restore limit 107,520K (high-end-gfx)
cached pss
这个名字会导致误解,看源码实现则很清晰,其实这就是
cached
优先级进程所占用的PSS
内存
cached kernel
这个才是真正的
cache
内存:
mInfos[Debug.MEMINFO_BUFFERS] + kReclaimable + + mInfos[Debug.MEMINFO_CACHED]
所以你会发现其实都是/proc/meminfo
下各种数据的组合
used pss
就是把系统中除去
cached
优先级的所有进程的PSS
相加起来
kernel
这相当于
kernel
的统计内存,包含Shmem
、SUnreclaim
、通过vmalloc
分配且为VM_ALLOC
的部分…:/** * Amount of RAM that is in use by the kernel for actual allocations. */ public long getKernelUsedSizeKb() { long size = mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE] + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES]; if (!Debug.isVmapStack()) { size += mInfos[Debug.MEMINFO_KERNEL_STACK]; } return size; }
同时还会将
Unmapped
类型的dmabuf
或者是gpuPrivateUsage
ZRAM
可以看到
ZRAM
的配置大小,使用大小,占用的实际内存大小,可以看到ZRAM
的大概压缩率为25%,即可剩下75%的内存
dumpsys meminfo
分析单个进程内存占用情况的工具,
c@M:~$ adb shell dumpsys meminfo 10643
Applications Memory Usage (in Kilobytes):
Uptime: 62386647 Realtime: 62386647
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 189744 189584 160 231032 189744 0 0 0
Dalvik Heap 0 0 0 0 0 0 0 0
Stack 6960 6700 260 1044 6960
Other dev 19 0 4 0 480
.so mmap 83064 18904 60296 2064 94164
Other mmap 73 48 24 0 512
Unknown 18916 18884 32 7304 18916
TOTAL 540220 234120 60776 241444 310776 0 0 0
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 0 0
Native Heap: 189584 189744
Code: 79200 94164
Stack: 6700 6960
Graphics: 0 0
Private Other: 19412
System: 245324
Unknown: 19908
TOTAL PSS: 540220 TOTAL RSS: 310776 TOTAL SWAP PSS: 241444
主要看App Summary PSS
,该进程属于Native
进程,所以Java heap
为0
,Native heap
主要是一些堆内存,System
则是ToTal PSS -Total Private Clean - Total Private Dirty
,其中Total PSS
包含了Total SWAP PSS
;Privte
即为该进程独占的内存
值得注意的是,dumpsys meminfo Graphics
内存还比较准确,其数据来源是分析/proc/
,但是对于Graphics
则需要分析其他文件,比如高通平台的GPU
以及ION
内存就不全包含其中。
memcg(Memory Cgroup)
meminfo内核文档