Linux内存回收与swap

在Linux系统中,每个进程都独有一个虚拟地址空间(Virtual Address Space),由内核维护内存映射。为完成内存映射(虚拟内存地址映射到物理内存地址),内核为每一个进程维护一张页表。而页表存储在CPU的内存管理单元MMU中(即通过硬件完成了内存地址的查找)。

缺页异常

当进程访问的虚拟地址在页表中找不到时,就会产生缺页异常。此时会陷入内核空间完成物理内存分配、更新进程页表,然后恢复进程运行。

MMU以页为单位来管理内存,每个页通常为4KB大小,所以每一次内存映射需要关联4KB的整数倍的内存空间。

为了解决页表项过大问题,Linux提供多级页表(实际上是四级页表)和大页(HugePage)机制。

Linux虚拟地址空间分布

Linux的虚拟地址空间,大大增加了进程的寻址空间,32位系统的虚拟地址空间分布如图:

Linux内存回收与swap_第1张图片

  • 只读段:只读,不可写;包括代码段、常量
  • 读/写段(或数据段):保存全局变量、动态变量
  • 堆:即平时说的动态内存,自下往上增长;堆内存由应用程序自己分配管理,依靠应用程序进行释放,但内核可将不常用数据换出到swap。大于128K的内存通过malloc()函数来分配(使用unmap()来释放),堆顶位置小于128K的内存通过brk()来分配(使用free()来释放)。
  • 文件映射区域:动态库、共享内存等映射物理空间的内存,自上往下增长;一般为mmap()函数所分配的虚拟地址空间
  • 栈:用于维护函数调用的上下文空间,一般为8M,ulimit -s查看
  • 内核虚拟空间:内核管理,对用户代码不可见。内核空间与用户空间的边界由TASK_SIZE控制。

对于64为Linux,内核空间占据顶部128T的虚拟地址空间,用户空间占据底部128T的虚拟地址空间。中间部分是未定义的。

另一张Linux虚拟地址空间分布图:

Linux内存回收与swap_第2张图片

Linux内存回收

通过Linux虚拟地址空间分布,我们可以看出,Linux内存回收主要针对堆和文件映射区域。

  • 文件页:cache、buffer,通过内存映射的文件映射页
  • 匿名页:堆内存
  • 目录缓存和inode缓存

Linux内存回收时机

Linux提供2种内存回收时机:

  • 直接内存回收
  • 内核线程回收

直接内存回收

当发生内存分配,而此时的剩余空闲内存不足时,会执行立即内存回收。

内核线程回收

内核线程kswapd会定期扫描内存使用量,当剩余内存小于pages_free_low的值时,会进行内存回收,并确保可用内存高于pages_free_high。可通过修改/proc/sys/vm/min_free_kbytes文件的值来修改pages_free_min的值,另外两个值会自动计算得出。

Linux内存回收与swap_第3张图片
NUMA架构中,每一个NUMA节点都会有一个kswapd线程。可通过numactl --hardware命令查看NUMA节点信息。

# cat /proc/sys/vm/min_free_kbytes
90112
# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 20 21 22 23 24 25 26 27 28 29
node 0 size: 32642 MB
node 0 free: 188 MB
node 1 cpus: 10 11 12 13 14 15 16 17 18 19 30 31 32 33 34 35 36 37 38 39
node 1 size: 32768 MB
node 1 free: 68 MB
node distances:
node   0   1 
  0:  10  21 
  1:  21  10

Linux内存回收手段

Linux通过以下方式进行内存回收:

  1. 通过LRU算法回收文件页缓存:包括cache、buffer,通过内存映射的文件映射页;对于干净缓存直接回收、脏页回写到磁盘进行回收
  2. 程序不常访问的匿名页:通过swap机制回收堆内存
  3. OOM杀死oom_score大的程序:内存占用越大,oom_score越大,越容易被OOM;cpu占用越多,oom_score越小,越不容易被OOM;可通过/proc/$PID/oom_adj来影响oom_score的值,取值范围[-17, 15]
  4. 手动回收:通过修改/proc/sys/vm/drop_caches的值进行手动回收,最好再执行下sync命令回写脏页
    • 0:释放pagecache(即文件页)
    • 1:释放目录缓存和inodes
    • 3:释放pagecache(即文件页)、目录缓存和inodes

swappiness内存回收倾向

通过修改/proc/sys/vm/swappiness文件的值,使系统更倾向于回收匿名页或文件页,取值范围[0, 100]。
数值越大,越倾向于回收匿名页,越积极使用swap;
数值越小,越倾向于回收文件页,越消极使用swap;
但即使设置为0,仍然会使用swap。

NUMA

NUMA架构中,多处理器被划分到不同Node上,而同一个Node又进一步被换分为不同的内存域(Zone)。
在Linux中,默认开启NUMA,在我的系统(rhel6.5)上,64G的内存被划分为2个Node

# yum install -y numactl
# numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 2 4 6 8 10 12 14
node 0 size: 32757 MB
node 0 free: 237 MB
node 1 cpus: 1 3 5 7 9 11 13 15
node 1 size: 32767 MB
node 1 free: 137 MB
node distances:
node   0   1 
  0:  10  20 
  1:  20  10

当某node内存不足时,系统可以选择从其他node寻找空间内存,也可以选择从本node回收内存。具体偏好可以通过/proc/sys/vm/zone_reclaim_mode文件来控制。

  • 0:同时回收其他node和本node内存
  • 1:回收本node内存
  • 2:写脏数据回收本node内存
  • 4:匿名页回收本node内存

关闭NUMA

可以通过修改内核启动参数来禁用MUNA。

vi /boot/grub/grub.conf
...
kernel /boot/vmlinuz-2.6.18-128.1.16.0.1.el5 root=LABEL=DBSYS ro bootarea=dbsys rhgb quiet console=ttyS0,115200n8 console=tty1 crashkernel=128M@16M numa=off
...

swap查看

可通过cat /proc/$PID/status | grep VmSwap查看进程使用的swap情况。

也可通过smem命令查看进程使用的swap量。

查看swap使用最大的前10个进程:

# smem --sort swap -r | head -10
  PID User     Command                         Swap      USS      PSS      RSS 
12076 999      mysqld                        446756    60560    60894    61232 
22757 mysql    /usr/sbin/mysqld               61236     4360     4471     5520 
11864 root     /usr/bin/dockerd -H fd:// -    24848    16212    16212    16216 
  588 root     /usr/bin/containerd            13064     9688     9688     9692 
  590 root     /usr/bin/python3 /usr/share     7996        8        8       12 
  515 root     /usr/bin/python3 /usr/bin/n     7840        4        4        8 
12225 root     php-fpm: master process (/u     5988       52       71      116 
 4305 root     -bash                           3644     1852     2030     3196 
12368 www-data php-fpm: pool ww                3076     4668     6913     9184

查看每个用户的使用率

# smem -u
User     Count     Swap      USS      PSS      RSS 
nagios       1        0      704      728     1500 
chrony       1        0      700      885     2040 
dbus         1        0      892     1123     2508 
nobody      16        0     8832     9712    34300 
polkitd      1        0    11236    12009    14352 
root        28        0   194864   218192   269140

查看某进程(正则表达式匹配进程名):

# smem -P nginx
  PID User     Command                         Swap      USS      PSS      RSS 
 1423 root     nginx: master process /usr/        0      152      186     1160 
 1425 nobody   nginx: worker process              0      552      607     2144 
 1426 nobody   nginx: worker process              0      552      607     2144 
 1427 nobody   nginx: worker process              0      552      607     2144 
 1428 nobody   nginx: worker process              0      552      607     2144 
 1429 nobody   nginx: worker process              0      552      607     2144 
 1430 nobody   nginx: worker process              0      552      607     2144 
 1431 nobody   nginx: worker process              0      552      607     2144 
 1434 nobody   nginx: worker process              0      552      607     2144 
 1435 nobody   nginx: worker process              0      552      607     2144 
 1436 nobody   nginx: worker process              0      552      607     2144 
 1437 nobody   nginx: worker process              0      552      607     2144 
 1438 nobody   nginx: worker process              0      552      607     2144 
 1439 nobody   nginx: worker process              0      552      607     2144 
 1440 nobody   nginx: worker process              0      552      607     2144 
 1441 nobody   nginx: worker process              0      552      607     2144 
 1442 nobody   nginx: worker process              0      552      607     2140 
 9278 root     python /usr/bin/smem -P ngi        0     4628     5319     6820

nocache软件包

nocache软件包提供cachestatscachedel命令行工具,可查看、清理某文件在内存中的缓存。
bcc软件包中也提供一个cachestats命令行工具,可提供整个操作系统缓存的读写命中情况,另外还包含cachetop来提供每个进程的缓存命中情况。安装bcc软件包可能需要升级内核至4.1。

root@blog:~# cachestats /root/holiday.py 
pages in cache: 0/1 (0.0%)  [filesize=2.4K, pagesize=4K]
root@blog:~# cat /root/holiday.py &> /dev/null
root@blog:~# cachestats /root/holiday.py 
pages in cache: 1/1 (100.0%)  [filesize=2.4K, pagesize=4K]
root@blog:~# cachedel /root/holiday.py
root@blog:~# cachestats /root/holiday.py 
pages in cache: 0/1 (0.0%)  [filesize=2.4K, pagesize=4K]

/proc/sys/vm目录

/proc/sys/vm目录包含内存管理调优、buffer、cache管理的文件,这些参数用来调整Virtual Memory子系统的行为以及数据的写出(从RAM到ROM),具体如下。

文件 默认值 含义
/proc/sys/vm/admin_reserve_kbytes 3%的free pages与8MB中较小的值 系统中为拥有cap_sys_admin的权限(可以大致理解到root权限)的user预留的free memory数量。增大该值以保证root可登录并恢复系统。该值的修改需要考虑root登录后使用的工具(ssh、bash、ps、top、netstat等),一般推荐VSS+RSS。
/proc/sys/vm/block_dump 0,禁用 是否打开Block Debug模式,用于记录所有的读写及Dirty Block写回动作。
/proc/sys/vm/dirty_background_bytes 0 与dirty_background_ratio互斥,不可同时使用,设置了dirty_background_ratio之后,此项会变成0; 该文件定义脏数据的量,此时触发后台内核线程把脏数据写回磁盘
/proc/sys/vm/dirty_background_ratio 10 与dirty_background_bytes互斥,不可同时使用; 脏数据所占的页面,占包括free页面和可回收页面的在内的总可用页面数的百分比,此时触发后台内核线程将脏数据写回磁盘。这里说的总可用内存不等于系统的总内存。
/proc/sys/vm/dirty_expire_centisecs 3000(单位是1/100秒,即3s) 该文件表示如果脏数据在内存中驻留时间超过该值,后台内核线程会在下一次将把这些数据写回磁盘。
/proc/sys/vm/dirty_ratio 40 该文件表示如果进程产生的脏数据所占页面,占包括free页面和可回收页面的在内的总可用页面数的百分比,磁盘写操作的进程会自动把脏数据写回磁盘
/proc/sys/vm/dirty_writeback_centisecs 500(单位是1/100秒,即0.5s) 该文件表示后台内核线程周期性间隔多久把脏数据写回磁盘(即多少间隔唤醒一次)。
/proc/sys/vm/drop_caches 0 写入文件对应的值可使内核回收释放pagecache(即文件页,包括cache、buffer、通过内存映射的文件映射页)、目录缓存和inodes,从而得到更多空闲内存。 该操作是非破坏性的,脏数据不会被释放,应该在运行之前执行sync命令将所有缓存对象释放。 1:释放pagecache; 2:释放目录缓存和inodes; 3:释放pagecache、目录缓存和inodes;
/proc/sys/vm/hugetlb_shm_group 0 该文件表示允许使用hugetlb页创建System VIPC共享内存段的系统组ID。
/proc/sys/vm/legacy_va_layout 0 该文件表示是否使用最新的32位共享内存mmap()系统调用,Linux支持的共享内存分配方式包括mmap(),Posix,System VIPC。0,使用最新32位mmap()系统调用。1,使用2.4内核提供的系统调用。
/proc/sys/vm/min_free_kbytes 724(512M物理内存) 该文件表示强制Linux VM最低保留多少空闲内存(Kbytes)。
/proc/sys/vm/min_slab_ratio 5(%) 仅适用NUMA架构中; zone中可回收页面占总页面的百分比,超过后则进行回收; slab回收是基于每个zone/node的方式来触发的,这个过程可能不太快;
/proc/sys/vm/nr_hugepages 0 该文件表示系统保留的hugetlb页数。
/proc/sys/vm/nr_pdflush_threads 2(只读) 该文件表示当前正在运行的pdflush进程数量,在I/O负载高的情况下,内核会自动增加更多的pdflush进程。
/proc/sys/vm/overcommit_memory 0 该文件指定了内核针对内存分配的策略,其值可以是0、1、2。0,表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。1,表示内核允许分配所有的物理内存,而不管当前的内存状态如何。2,表示内核允许分配超过所有物理内存和交换空间总和的内存(参照overcommit_ratio)
/proc/sys/vm/overcommit_ratio 50(%) 该文件表示,如果overcommit_memory=2,可以过载内存的百分比,通过以下公式来计算系统整体可用内存。系统可分配内存=交换空间+物理内存*overcommit_ratio/100
/proc/sys/vm/page-cluster 3(2的3次方,8页) 该文件表示在写一次到swap区的时候写入的页面数量,0表示1页,1表示2页,2表示4页。
/proc/sys/vm/swapiness 60 该文件表示系统进行交换行为的程度,数值(0-100)越高,越可能发生磁盘交换。
/proc/sys/vm/vfs_cache_pressure 100 该文件表示内核回收用于directory和inode cache内存的倾向;缺省值100表示内核将根据pagecache和swapcache,把directory和inode cache保持在一个合理的百分比;降低该值低于100,将导致内核倾向于保留directory和inode cache;增加该值超过100,将导致内核倾向于回收directory和inode cache。

/proc/sys/vm中各文件含义大部分摘自:
https://www.cnblogs.com/xianbei/archive/2012/11/23/2783818.html

/proc/sys/vm中各文件含义部分内容参考:
https://blog.csdn.net/cassie_huang/article/details/80633328

smem命令:
https://linux.cn/article-4492-1.html

其他参考:

https://blog.csdn.net/weixin_33800593/article/details/90681825

https://cloud.tencent.com/developer/article/1004428

https://cloud.tencent.com/developer/article/1004429

你可能感兴趣的:(Linux系统原理,内存,swap)