/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
:块设备的
Bufferspage 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
memcg(Memory Cgroup)
meminfo内核文档
原文作者:ora___
原文地址:http://t.csdn.cn/K3I3L(版权归原文作者所有,侵权留言联系删除)