介绍

  • 列举一下/proc/meminfo文件的各个项的含义
  • 一般情况下如何粗略统计内存占用情况
  • 以下文件或者图片显示均基于内核6.8.0-55-generic x86_64版本

查看Linux整体内存的命令

1
2
3
4
# free
total used free shared buff/cache available
Mem: 3971780 767272 211712 17116 3167716 3204508
Swap: 3966972 39636 3927336

free 命令默认单位是KB,可以通过-h参数自动计算成合适的单位

  • total: 内存总数
  • used: 当前已经用掉的
  • free: 还剩下的空闲内存
  • shared: 共享内存占用
  • buff/cache: 磁盘块缓存和文件缓存占用
  • available: 当前还可以使用的内存(由于缓存可释放,所以实际还可以使用多少内存看的是此项)
1
2
3
4
5
6
# top -b -n 1 | head -n 5
top - 21:48:51 up 93 days, 4:31, 2 users, load average: 0.00, 0.00, 0.00
Tasks: 164 total, 1 running, 163 sleeping, 0 stopped, 0 zombie
%Cpu(s): 4.3 us, 4.3 sy, 0.0 ni, 91.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 3878.7 total, 203.1 free, 752.6 used, 3093.8 buff/cache
MiB Swap: 3874.0 total, 3835.3 free, 38.7 used. 3126.1 avail Mem

top命令其实看的是整个系统的负载情况,当前这里只关注内存相关
所以只需要查看MiB MemMiB Swap两行即可

  • total: 内存总数
  • free: 还剩下的空闲内存
  • used: 当前已经用掉的
  • buff/cache: 磁盘块缓存和文件缓存占用

有时候硬件上区分多个numa区域,可以通过numactl命令查看每个域上内存占用情况

1
2
3
4
5
6
7
8
# numactl -H
available: 1 nodes (0)
node 0 cpus: 0 1
node 0 size: 3878 MB
node 0 free: 203 MB
node distances:
node 0
0: 10
  • node 0 cpus: node 0上的cpu资源
  • node 0 size: node 0上的内存总大小
  • node 0 free: node 0上还剩下的空闲内存
1
2
3
4
# head -n 3 /proc/meminfo
MemTotal: 3971780 kB
MemFree: 211804 kB
MemAvailable: 3205284 kB

这里因为只关注整体,所以只看前三项

  • MemTotal: 内存总数
  • MemFree: 还剩下的空闲内存
  • MemAvailable: 当前还可以使用的内存(由于缓存可释放,所以实际还可以使用多少内存看的是此项)

sar命令的结果是一段时间内内存占用的日志,而不是当前实时内存情况表现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# sar -r
Linux 6.8.0-55-generic (develop) 07/07/25 _x86_64_ (2 CPU)

06:08:08 kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty
06:10:13 627160 3147336 527700 13.29 162072 2043520 1992380 25.10 1244812 1355896 172
06:20:23 639812 3160340 515016 12.97 162288 2043656 1955752 24.64 1234956 1356112 100
06:30:13 642004 3162740 512652 12.91 162496 2043664 1947384 24.53 1229244 1356336 104
06:40:07 642608 3163524 511868 12.89 162664 2043676 1947384 24.53 1229464 1356512 84
06:50:03 641852 3162992 512396 12.90 162888 2043684 1947384 24.53 1229020 1356732 176
07:00:23 640616 3162120 513264 12.92 163112 2043824 1947384 24.53 1229636 1356968 100
07:10:13 638600 3160296 515100 12.97 163284 2043836 1947384 24.53 1229124 1357160 104
07:20:23 640336 3162272 513112 12.92 163512 2043844 1947384 24.53 1229368 1357396 168
07:30:03 643028 3165192 510196 12.85 163724 2043852 1947384 24.53 1228936 1357616 200
07:40:11 642020 3164412 510972 12.87 163928 2043872 1947384 24.53 1229248 1357836 144
07:50:13 641780 3164496 510896 12.86 164132 2043984 1947384 24.53 1229704 1358048 84
08:00:23 642660 3165552 509836 12.84 164288 2044000 1947384 24.53 1229328 1358212 100
08:10:19 642156 3165280 510136 12.84 164500 2044012 1947384 24.53 1229664 1358432 100
08:20:23 643108 3166452 508960 12.81 164700 2044024 1947384 24.53 1229208 1358644 168
08:30:13 645108 3168732 506684 12.76 164904 2044096 1947384 24.53 1229568 1358856 88
08:40:11 651092 3174920 500492 12.60 165092 2044104 1947384 24.53 1229064 1359052 96
08:50:23 649680 3173844 501564 12.63 165280 2044244 1947384 24.53 1229600 1359252 152
09:00:23 649972 3174392 501020 12.61 165516 2044256 1947384 24.53 1229948 1359500 156
09:10:13 650688 3175352 500052 12.59 165740 2044272 1947384 24.53 1229412 1359740 88
  • kbmemfree: 剩余空闲没有使用的内存,单位KB
  • kbavail: 当前还可以使用的内存,包括空闲和可回收的内存,单位KB
  • kbmemused: 当前已经使用的内存,单位KB
  • %memused: 当前使用内存占总内存的百分比
  • kbbuffers: Buffer缓存,单位KB
  • kbcached: Cached缓存,单位KB
  • kbcommit: 当前工作负载所需的内存量。这是对需要多少RAM/交换来保证永远不会出现内存不足的估计值,单位KB
  • %commit: 当前工作负载所需的内存占内存总量(RAM+交换)的百分比。这个数字可能大于100%,因为内核通常会超量使用内存。
  • kbactive: 活动内存,单位KB(最近使用的内存,除非绝对必要,否则通常不会回收)
  • kbinact: 非活动内存,单位KB(以千字节为单位)(最近使用较少的内存。它更有资格被回收用于其他目的)
  • kbdirty: 等待写回磁盘的内存,单位KB

meminfo

cat /proc/meminfo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
MemTotal:        3971780 kB
MemFree: 229344 kB
MemAvailable: 3218196 kB
Buffers: 148652 kB
Cached: 2595604 kB
SwapCached: 6936 kB
Active: 874760 kB
Inactive: 2229660 kB
Active(anon): 198364 kB
Inactive(anon): 51200 kB
Active(file): 676396 kB
Inactive(file): 2178460 kB
Unevictable: 39348 kB
Mlocked: 39348 kB
SwapTotal: 3966972 kB
SwapFree: 3927336 kB
Zswap: 0 kB
Zswapped: 0 kB
Dirty: 236 kB
Writeback: 0 kB
AnonPages: 248648 kB
Mapped: 214408 kB
Shmem: 17072 kB
KReclaimable: 419484 kB
Slab: 537100 kB
SReclaimable: 419484 kB
SUnreclaim: 117616 kB
KernelStack: 4736 kB
PageTables: 8640 kB
SecPageTables: 0 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 5952860 kB
Committed_AS: 1582636 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 29260 kB
VmallocChunk: 0 kB
Percpu: 1624 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
FileHugePages: 0 kB
FilePmdMapped: 0 kB
Unaccepted: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 173104 kB
DirectMap2M: 3983360 kB
DirectMap1G: 2097152 kB

MemTotal

系统中可用内存的总大小 不是物理内存条大小,物理内存减去BIOS使用和内核命令行中预留的大小

系统从加电开始到引导完成,BIOS等要保留一些内存,内核要保留一些内存,最后剩下可供系统支配的内存就是MemTotal。这个值在系统运行期间一般是固定不变的

MemFree

系统中当前还没有使用的内存 完全空闲的内存,可以直接被申请并且使用,[MemTotal-MemFree]就是已被用掉的内存

MemAvailable

当前可以被使用的内存 由于环境上一些当前没有被占用的缓存可以被释放回收掉,比如一部分buffer/cache,以及一些slab

所以即使完全空闲的内存比较少,但是进程可以申请的内存也可以很多

Buffers

磁盘设备的块缓存 为了提高磁盘读写效率而存在,将分散读写尽可能合并下发IO

这里面包含:直接读写块设备、以及文件系统元数据(metadata)比如SuperBlock所使用的缓存页。它与“Cached”的区别在于,”Cached”表示普通文件所占用的缓存页
“Buffers”表示块设备(block device)所占用的缓存页,包括:直接读写块设备、以及文件系统元数据(metadata)比如SuperBlock所使用的缓存页
“Buffers”所占的内存同时也在LRU list中,被统计在Active(file)或Inactive(file)。

Cached

文件的页缓存 为了提高文件读写速率而存在,读取文件时,将文件先加载到内存,以便于做到快速访问

Cached也包含tmpfs中的文件,POSIX/SysV shared memory,以及shared anonymous mmap。
注:POSIX/SysV shared memory和shared anonymous mmap在内核中都是基于tmpfs实现的,参见:tmpfs

与Mapped的关系

Cached是”Mapped”的超集,就是说它不仅包括mapped,也包括unmapped的页面,当一个文件不再与进程关联之后,原来在page cache中的页面并不会立即回收,仍然被计入Cached,还留在LRU中,但是 Mapped 统计值会减小。【ummaped = (Cached – Mapped)】

SwapCached

交换分区中的缓存 当前在swap中但标记为交换回内存的页面数量

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

匿名页(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的目的就是为了腾出空闲内存;
  • 曾经被swap-out现在又被swap-in的匿名页会在swap cache中,直到页面中的内容发生变化、或者原来用过的交换区空间被回收为止。

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

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

LRU

页面回收算法机制 LRU是Kernel的页面回收算法(Page Frame Reclaiming)使用的数据结构。Page cache和所有用户进程的内存(kernel stack和huge pages除外)都在LRU lists上。

LRU lists包括如下几种,在/proc/meminfo中都有对应的统计值:

  • LRU_ACTIVE_ANON – 对应 Active(anon)

    活跃的匿名页 比如最近使用过或者正在用的进程的堆栈,用malloc申请的内存
  • LRU_INACTIVE_ANON – 对应 Inactive(anon)

    非活跃的匿名页 比如早之前用过,最近没再用的进程的堆栈,用malloc申请的内存
  • LRU_ACTIVE_FILE – 对应 Active(file)

    活跃的与文件关联的内存 比最近使用过或者正在用的如程序文件、数据文件等
  • LRU_INACTIVE_FILE – 对应 Inactive(file)

    非活跃的与文件关联的内存 比如早之前用过,最近没再用的程序文件、数据文件等
  • LRU_UNEVICTABLE – 对应 Unevictable

    不可触达内存,通常是被内核保留用于特定目的内存区域,比如内核锁定的内存页:
    • Those owned by ramfs.
    • Those owned by tmpfs with the noswap mount option.
    • Those mapped into SHM_LOCK’d shared memory regions.
    • Those mapped into VM_LOCKED [mlock()ed] VMAs.
  • Active

    活跃内存页 在活跃使用中的缓冲或高速缓冲存储器页面文件的大小,除非非常必要否则不会被移作他用。其值为活跃匿名页Active(anon) + 活跃文件Active(file)
  • Inactive

    非活跃的内存页 在不经常使用中的缓冲或高速缓冲存储器页面文件的大小,优先会被回收用作他用。其值为非活跃匿名页Inactive(anon) + 非活跃文件Inactive(file)
  • Inactive list里的是长时间未被访问过的内存页,Active list里的是最近被访问过的内存页,LRU算法利用Inactive list和Active list可以判断哪些内存页可以被优先回收。
  • 括号中的 anon 表示匿名页(anonymous pages)。
  • 括号中的 file 表示 file-backed pages(与文件对应的内存页)。
  • 用户进程的内存页分为两种:file-backed pages(与文件对应的内存页),和anonymous pages(匿名页),比如进程的代码、映射的文件都是file-backed,而进程的堆、栈都是不与文件相对应的、就属于匿名页。file-backed pages在内存不足的时候可以直接写回对应的硬盘文件里,称为page-out,不需要用到交换区(swap);而anonymous pages在内存不足时就只能写到硬盘上的交换区(swap)里,称为swap-out。
  • Unevictable LRU list上是不能pageout/swapout的内存页,包括VM_LOCKED的内存页、SHM_LOCK的共享内存页(又被统计在”Mlocked”中)、和ramfs。在unevictable list出现之前,这些内存页都在Active/Inactive lists上,vmscan每次都要扫过它们,但是又不能把它们pageout/swapout,这在大内存的系统上会严重影响性能,设计unevictable list的初衷就是避免这种情况,参见:unevictable-lru
  • LRU与/proc/meminfo中其他统计值的关系:
    • LRU中不包含HugePages_*。
    • LRU包含了 Cached 和 AnonPages。

Mlocked

被锁定的内存 主要是mlock系统调用处理的内存(VM_LOCKED标记),global_zone_page_state(NR_MLOCK)

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

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

SwapTotal

交换空间的总大小 `swapon`/`swapoff`创建的交换区大小,交换分区是硬盘上的一块区域,当物理内存不够时,OS将一部分内存数据交换到swap分区中,以释放物理内存供其他程序使用

SwapFree

未被使用交换空间的大小 剩下可以使用的交换区的大小

Zswap

压缩缓冲区的大小

zswap是一种内存压缩缓存机制,位于内存和磁盘交换空间之间。当内存需要交换到磁盘时,zswap会先将内存页压缩后存储在zswap缓存中,而不是直接写入磁盘。这种方式可以提高性能,因为压缩后的数据占用的空间更小,并且可以更快地访问。当zswap缓存满后,会按照最近最少使用(LRU)的顺序将最旧的页面解压后写入磁盘,然后将新的页面压缩后存入zswap缓存。

Zswapped

已经压缩的内存占用

Dirty

等待被写回到磁盘的内存大小

文件系统的页面写过后会被标记为脏页,该内存会统计进入这里,脏页不会立马写入磁盘,而是等到触发条件后才会下发IO进行同步数据到实际设备中,比如磁盘。
当脏页开始下发时,该页面的统计会从Dirty转移到Writeback

Writeback

正在被写回到磁盘的内存大小 脏页的IO下发期间会被统计至这里,同步完成后从这里清除

AnonPages

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

所有page cache里的页面(Cached)都是file-backed pages,不是Anonymous PagesCachedAnoPages之间没有重叠。

  • 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

正在被使用的映射到用户空间的内存大小 通常是页面缓存`Cached`的一部分,Mapped 统计了页面缓存中被实际使用的部分,而这些部分被用户空间中的进程所引用。

Page cache中Cached包含了文件的缓存页,其中有些文件当前已不在使用,page cache仍然可能保留着它们的缓存页面;而另一些文件正被用户进程关联,比如shared libraries、可执行程序的文件、mmap的文件等,这些文件的缓存页就称为Mapped
Mapped就统计了page cache(Cached)中所有的mapped页面。MappedCached的子集。
Linux系统上shared memory & tmpfs被计入page cache(Cached),所以被attached的shared memory、以及tmpfs上被map的文件都算做Mapped

Shmem

共享内存的统计

Shmem统计的内容包括:

  • shared memory
  • tmpfsdevtmpfs

    所有tmpfs类型的文件系统占用的空间都计入共享内存,devtmpfs是/dev文件系统的类型,/dev/下所有的文件占用的空间也属于共享内存。可以用ls和du命令查看。如果文件在没有关闭的情况下被删除,空间仍然不会释放,shmem不会减小,可以用 lsof -a +L1 /mount_point 命令列出这样的文件。

这里的shared memory又包括:

  • SysV shared memory [shmget etc.]
  • POSIX shared memory [shm_open etc.]
  • shared anonymous mmap [ mmap(…MAP_ANONYMOUS|MAP_SHARED…)]

因为shared memory在内核中都是基于tmpfs实现的,参见:tmpfs
也就是说它们被视为基于tmpfs文件系统的内存页,既然基于文件系统,就不算匿名页,所以不被计入/proc/meminfo中的AnonPages,而是被统计进了:

  • Cached (i.e. page cache)
  • Mapped (当shmem被attached时候)
    然而它们背后并不存在真正的硬盘文件,一旦内存不足的时候,它们是需要交换区才能swap-out的,所以在LRU lists里,它们被放在:
    • Inactive(anon)Active(anon)

      虽然它们在LRU中被放进了anon list,但是不会被计入 AnonPages。这是shared memory & tmpfs比较拧巴的一个地方,需要特别注意。

    • unevictable (如果被locked的话)

当shmget/shm_open/mmap创建共享内存时,物理内存尚未分配,要直到真正访问时才分配。/proc/meminfo中的 Shmem 统计的是已经分配的大小,而不是创建时申请的大小

KReclaimable

内核可回收内存 这里的值是`SReclaimable` + `NR_KERNEL_MISC_RECLAIMABLE`(其他可回收的内核数据结构)

Slab

通过内核Slab接口分配的内存

通过slab分配的内存被统计在以下三个值中:

  • SReclaimable: slab中可回收的部分。调用kmem_getpages()时加上SLAB_RECLAIM_ACCOUNT标记,表明是可回收的,计入SReclaimable,否则计入SUnreclaim。
  • SUnreclaim: slab中不可回收的部分。
  • Slab: slab中所有的内存,等于以上两者之和。

详细接口内存申请可以查看/proc/slabinfo文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
slabinfo - version: 2.1
# name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
SCTPv6 21 21 1536 21 8 : tunables 0 0 0 : slabdata 1 1 0
SCTP 46 46 1408 23 8 : tunables 0 0 0 : slabdata 2 2 0
cifs_small_rq 216 216 448 18 2 : tunables 0 0 0 : slabdata 12 12 0
cifs_request 20 20 16640 1 8 : tunables 0 0 0 : slabdata 20 20 0
cifs_inode_cache 45882 45882 872 18 4 : tunables 0 0 0 : slabdata 2549 2549 0
fscache_cookie_jar 0 0 176 23 1 : tunables 0 0 0 : slabdata 0 0 0
ovl_inode 4796 4796 720 22 4 : tunables 0 0 0 : slabdata 218 218 0
sw_flow 0 0 648 12 2 : tunables 0 0 0 : slabdata 0 0 0
nf_conntrack_expect 0 0 208 19 1 : tunables 0 0 0 : slabdata 0 0 0
nf_conntrack 144 144 256 16 1 : tunables 0 0 0 : slabdata 9 9 0
kvm_async_pf 0 0 136 30 1 : tunables 0 0 0 : slabdata 0 0 0
kvm_vcpu 0 0 9152 3 8 : tunables 0 0 0 : slabdata 0 0 0
kvm_mmu_page_header 0 0 184 22 1 : tunables 0 0 0 : slabdata 0 0 0
x86_emulator 0 0 2656 12 8 : tunables 0 0 0 : slabdata 0 0 0
ext4_groupinfo_4k 418 418 184 22 1 : tunables 0 0 0 : slabdata 19 19 0
btrfs_ordered_extent 0 0 416 19 2 : tunables 0 0 0 : slabdata 0 0 0
bio-312 12 12 320 12 1 : tunables 0 0 0 : slabdata 1 1 0
bio-376 21 21 384 21 2 : tunables 0 0 0 : slabdata 1 1 0
btrfs_path 0 0 112 36 1 : tunables 0 0 0 : slabdata 0 0 0
btrfs_trans_handle 0 0 168 24 1 : tunables 0 0 0 : slabdata 0 0 0
...

其中第三列num_objs和第四例objsize的乘积是该行结构申请的内存大小,单位字节

KernelStack

内核栈结构占用的内存 常驻内存,每一个用户线程都会有的一个数据结构

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

Kernel stack(内核栈)是常驻内存的,既不包括在LRU lists里,也不包括在进程的RSS/PSS内存里。

PageTables

管理内存分页页面的索引表的大小

Page Table用于将内存的虚拟地址翻译成物理地址,随着内存地址分配得越来越多,该值会增大。

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

SecPageTables

待定。。。

NFS_Unstable

NFS不稳定页表的大小 表示的是发送给NFS服务器但尚未写入磁盘的缓存大小。它是指那些已经从本地文件系统提交到NFS服务器,但还没有被NFS服务器成功写入硬盘的数据所占用的内存。

当客户端将数据写入NFS共享目录时,数据首先会缓存在客户端的内存中,然后异步地发送到NFS服务器。NFS_Unstable统计的就是这些已发送但尚未写入NFS服务器硬盘的缓存数据大小。
此值的存在是为了提高NFS 客户端的写入性能。它允许客户端快速地完成写入操作,而不用等待数据真正写入服务器硬盘。但是这个数据是“不稳定”的,因为如果NFS服务器在写入完成之前宕机,这些数据可能会丢失。因此,当客户端需要确保数据安全时,通常会使用同步写入(sync write) 模式,然而这又会降低写入性能。

Bounce

块设备的Bounce buffer占用的内存

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

WritebackTmp

FUSE临时回写缓冲区的内存 这个值和`Writeback`很像,两者的区别待定。。。

Memory used by FUSE for temporary writeback buffers.

CommitLimit

系统允许申请的最大内存大小 表示系统当前可以承诺分配的最大内存量,包括已分配但尚未实际使用的内存。

它代表了系统允许的最大内存申请量,这个限制受vm.overcommit_memoryvm.overcommit_ratio等参数的影响。
vm.overcommit_memory控制着内存分配策略,而vm.overcommit_ratio决定了可以分配的内存与物理内存和交换空间之和的比例。

vm.overcommit_memory:
这个参数决定了Linux 如何处理内存分配请求。

  • 0: 表示启发式过度提交,内核会根据一定的算法来判断是否允许内存分配。
  • 1: 表示总是允许内存分配,即使这可能导致内存耗尽。
  • 2: 表示只允许分配的内存不超过CommitLimit,即不允许过度提交。

vm.overcommit_ratio:
这个参数定义了允许分配的内存与物理内存和交换空间之和的比例。
计算:CommitLimit =(overcommit_ratio * MemTotal)+ SwapTotal
例如,如果vm.overcommit_ratio设置为30,而系统有16GB的物理内存和8GB的交换空间,那么CommitLimit将是(16 * 0.3) + 8 = 12.8GB

只有当vm.overcommit_memory参数为2时,该值才会被使用

Committed_AS

目前在系统上分配的内存量 该值与`CommitLimit`是配对的。是所有进程申请的内存的总和,即使所有申请的内存没有被完全使用

当该值被启用时,CommitLimit减去Committed_AS的差值,代表了系统还可以分配的内存量。

VmallocTotal

内核为vmalloc 分配的虚拟内存空间总大小

vmalloc 提供了一种在内核中分配大块连续虚拟地址空间的方法,即使这些地址对应的物理内存不连续。这对于映射I/O 内存区域,或者在某些体系结构中需要连续虚拟地址来访问设备很有用。VmallocTotal则是内核预留的用于 vmalloc 分配的总虚拟地址空间大小。
在64 位系统中,该值通常被设置为一个非常大的值,例如32TB,表示内核预留了足够大的虚拟地址空间用于vmalloc
它并不一定与实际的物理内存大小直接对应。vmalloc分配的虚拟地址可能只映射到一部分物理内存,或者根本没有映射到物理内存(例如用于映射I/O 内存)。

VmallocUsed

实际使用 vmalloc 分配的内存大小 包括映射的物理内存部分和未映射的I/O 内存部分

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# sudo grep vmalloc /proc/vmallocinfo
...
0xffffa0ef00029000-0xffffa0ef0002b000 8192 gen_pool_add_owner+0x4b/0xf0 pages=1 vmalloc N0=1
0xffffa0ef0002c000-0xffffa0ef00031000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00031000-0xffffa0ef00033000 8192 gen_pool_add_owner+0x4b/0xf0 pages=1 vmalloc N0=1
0xffffa0ef00034000-0xffffa0ef00039000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00039000-0xffffa0ef0003b000 8192 gen_pool_add_owner+0x4b/0xf0 pages=1 vmalloc N0=1
0xffffa0ef0003c000-0xffffa0ef00041000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00044000-0xffffa0ef00049000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef0004c000-0xffffa0ef00051000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00054000-0xffffa0ef00059000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef0005b000-0xffffa0ef0005f000 16384 n_tty_open+0x19/0xb0 pages=3 vmalloc N0=3
0xffffa0ef00063000-0xffffa0ef00067000 16384 n_tty_open+0x19/0xb0 pages=3 vmalloc N0=3
0xffffa0ef0006c000-0xffffa0ef00071000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00071000-0xffffa0ef00073000 8192 bpf_prog_alloc_no_stats+0x42/0x290 pages=1 vmalloc N0=1
0xffffa0ef0007c000-0xffffa0ef00081000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00084000-0xffffa0ef00089000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef0008c000-0xffffa0ef00091000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
0xffffa0ef00094000-0xffffa0ef00099000 20480 dup_task_struct+0x5b/0x1b0 pages=4 vmalloc N0=4
...

其中第二列则是当前项申请的内存大小,单位字节
通过统计该文件中所有vmalloc类型的申请,就可以知道一共占用的内存大小:

1
sudo grep vmalloc /proc/vmallocinfo | awk '{sum+=$2} END {print sum}'

VmallocChunk

最大的连续未被使用的vmalloc区域

该值记录了 vmalloc 分配中剩余的可用的最大的连续虚拟地址空间的大小,默认为0

Percpu

内核percpu变量的内存占用

在内核中存在percpu的变量,它在每个cpu上都有一个拷贝,这样cpu在访问自己该变量时,不需要考虑与其他处理器竞争的问题,也就没必要加锁处理,同时还可以利用处理器本地的缓存提高访问速度,属于是空间换时间的做法

HardwareCorrupted

硬件故障的内存大小

当系统检测到内存的硬件故障时,会把有问题的页面删除掉,不再使用,该值统计了删除掉的内存页的总大小。
出现此种情况表明存在物理内存错误,这可能会导致系统不稳定、数据损坏,甚至系统崩溃。如果发现HardwareCorrupted值不为0,则需要及时更换内存条

Transparent HugePages

透明大页内存(THP) 是一种特殊的内存页,它们比普通页(4KB)大得多,通常为2MB或更大。是为了优化内存管理,提高性能的技术

与标准大页HugePages的区别是透明大页是申请的时候进行分配使用,并且由内核管理。

  • AnonHugePages 匿名大页内存的大小 代表着由进程使用但未映射到任何特定文件(即匿名)的大页内存,通常用于提升性能
    该值统计的是透明大页Transparent HugePages (THP),它与其他统计项有重叠,首先它被包含在AnonPages之中,而且在/proc/pid/smaps中也有单个进程的统计,与进程的RSS/PSS是有重叠的,如果用户进程用到了THP,进程的RSS/PSS也会相应增加,这与Hugepages是不同的。

    THP也可以用于shared memory和tmpfs,默认是禁止的,打开的方法如下(详见transhuge):
    mount时加上huge=always等选项
    通过/sys/kernel/mm/transparent_hugepage/shmem_enabled来控制
    因为默认情况下shared memorytmpfs不使用THP,所以进程之间不会共享AnonHugePages,于是就有以下等式:
    AnonHugePages == 所有进程的/proc/pid/smaps中AnonHugePages之和

  • ShmemHugePages 被用来映射共享内存和tmpfs内存的透明大页
  • ShmemPmdMapped
  • FileHugePages 用于文件映射的透明大页内存
  • FilePmdMapped

Unaccepted

待定。。。

HugePages

标准大页内存 `Huge Pages`是一种特殊的内存页,它们比普通页(4KB)大得多,通常为2MB或更大。使用Huge Pages可以减少页表项的数量,提高内存访问效率,它在物理地址空间上是保持连续的

它与透明大页Transparent HugePages的区别是,标准大页是提前预留好的,不计入其他统计项

大页内存Hugepages在/proc/meminfo中是被独立统计的,与其它统计项不重叠,既不计入进程的RSS/PSS中,又不计入LRU Active/Inactive,也不会计入cache/buffer。如果进程使用了Hugepages,它的RSS/PSS不会增加。
不要把透明大页跟标准大页搞混了,THP的统计值是/proc/meminfo中的AnonHugePages,在/proc/pid/smaps中也有单个进程的统计,这个统计值与进程的RSS/PSS是有重叠的,如果用户进程用到了THP,进程的RSS/PSS也会相应增加,这与Hugepages是不同的

  • HugePages_Total 配置的内存大页总数量
    对应内核参数vm.nr_hugepages,也可以在运行中的系统上直接动态修改/proc/sys/vm/nr_hugepages,修改的结果会立即影响空闲内存MemFree的大小,因为标准大页在内核中独立管理,只要一经定义,无论是否被使用,都不再属于free memory。
  • HugePages_Free 剩下还未使用的数量
  • HugePages_Rsvd 允许超出预留数量的个数
    承诺被分配但还未执行分配操作的HugePage数量
  • HugePages_Surp 实际使用掉的大页内存数量
    实际使用的数量有可能超出/proc/sys/vm/nr_hugepages。但最大不超过/proc/sys/vm/nr_overcommit_hugepages
  • Hugepagesize 每个大页内存的大小,一般默认2M
    当系统支持多种大小HugePage时,默认HugePage大小通过default_huagepaegsz指定。
  • Hugetlb 配置的大页内存的总大小

总结一下大页:
大页内存通过将相邻的小页面组合成大页面,减少了页表的数量,从而减少了内存开销和TLB(Translation Lookaside Buffer)的查询次数,提高了内存访问的效率。
普通的4K页面在页面转换时会存在两大开销

  • 时间开销:由于需要进行页表映射,从物理内存中寻找数据,而且还需要不断地更新页表,这些都需要额外耗费时间。
  • 空间开销:由于每个进程都需要使用自己的页表,所以每个进程都需要维护一个独立的页表,这样就会浪费很多空间。
    而大页内存可以减少内存管理的开销。相比于小页面,大页面意味着更少的页表项和更少的页面转换,这样就能减少CPU访问页表的时间以及缺页中断的次数,提高系统的整体性能。
    不过大页内存也有代价
    内存碎片:由于大页内存占用的空间更多,因此页表所使用的内存空间也会变得更大,进而导致内存碎片的增加。
    缺页处理开销:由于大页内存所占用的物理内存空间更大,如果该页内存中只有部分数据被使用,那么在发生页面错误时,操作系统需要将整个大页内存加载进入物理内存,这会增加缺页处理的时间开销。
    而且因为大页内存要保证物理地址连续,所以像透明大页在使用时所需要的代价是会随着系统内存碎片化程度的增加而增大的,具体原理解释见这篇博客:大页内存揭秘

DirectMap

TLB的效率指标

此项统计的不是关于内存的使用量,而是一个反映TLB效率的指标。TLB(Translation Lookaside Buffer)是位于CPU上的缓存,用于将内存的虚拟地址翻译成物理地址,由于TLB的大小有限,不能缓存的地址就需要访问内存里的page table来进行翻译,速度慢很多。
为了尽可能地将地址放进TLB缓存,新的CPU硬件支持比4k更大的页面从而达到减少地址数量的目的,比如2MB,4MB,甚至1GB的内存页。DirectMap4k表示映射为4kB的内存数量,DirectMap2M表示映射为2MB的内存数量,以此类推。所以DirectMap其实是一个反映TLB效率的指标。

  • DirectMap4k 使用4K内存的大小
  • DirectMap2M 使用2M内存的大小
  • DirectMap1G 使用1G内存的大小

参考