Swap 说白了就是把一块磁盘空间或者一个本地文件(以磁盘为例),当成内存来使用。它包括换出和换入两个过程:
1)换出,把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存。
2)换入,在进程再次访问这些内存的时候,把它们从磁盘读到内存中来
现在的内存便宜多,但 SWAP 依旧有用:
1)即使内存不足时,有些应用程序也并不想被 OOM 杀死,而是希望能缓一段时间,等待人工介入,或者等系统自动释放其他进程的内存,再分配给它
2)笔记本电脑的休眠和快速开机的功能,也基于 Swap 。休眠时,把系统的内存存入磁盘,这样等到再次开机时,只要从磁盘中加载内存就可以
在操作系统中,何时发生内存回收
直接内存回收
有新的大块内存分配请求,但是剩余内存不足。此时操作系统就需要回收一部分内存(比如缓存),进而尽可能地满足新内存请求
定期回收内存(kswapd0)
专门的内核线程,kswapd0,用来定期回收内存。
为了衡量内存的使用情况,kswapd0 定义三个内存阈值(watermark,也称为水位),分别是:页最小阈值(pages_min)、页低阈值(pages_low)、页高阈值(pages_high)
pages_free,表示剩余内存:
1)pages_free < pages_min,剩余内存小于页最小阈值,说明进程可用内存基本耗尽,只有内核才可以分配内存
2)pages_min < pages_free < pages_low,内存压力比较大,剩余内存不多,此时 kswapd0 会执行内存回收,直到 pages_free > pages_high 为止
3)pages_low < pages_free < pages_high,内存有一定压力,但还可以满足新内存请求
4)pages_high < pages_free,剩余内存比较多,没有内存压力
参数 pages_min 通过 /proc/sys/vm/min_free_kbytes 设置,pages_low = pages_min*5/4,pages_high = pages_min*3/2
NUMA 与 Swap
有时候可能会发现 Swap 升高,但是系统内存足够。这种情况可能是因为 处理器的 NUMA (Non-Uniform Memory Access) 架构导致的问题。
NUMA – Non-Uniform Memory Access
在 NUMA 架构下,多个处理器(逻辑处理器)被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间。
同个 Node 内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),比如直接内存访问区(DMA)、普通内存区(NORMAL)、伪内存区(MOVABLE)等,如图所示:
在 NUMA 下,每个 Node 都有自己的本地内存空间,那么在分析内存的使用时,我们也应该针对每个 Node 单独分析。
查看处理器在节点的分布情况
# numactl --hardware available: 1 nodes (0) node 0 cpus: 0 1 2 3 node 0 size: 15731 MB node 0 free: 160 MB node distances: node 0 0: 10 // 如上输出,系统只有一个 Node , // 在该 Node 内有 0 1 2 3 处理器 // 该 Node 的内存大小为 15731 MB // 剩余内存为 160 MB
NUMA 与 Swap 的关系
在 NUMA 下,是否使用 Swap,与特定 Zone 的内存使用情况有关,而不是整体内存使用情况。
通过内存域在 proc 文件系统中的接口 /proc/zoneinfo 来查看前面提到的三个内存阈值(页最小阈值、页低阈值、页高阈值):
# cat /proc/zoneinfo Node 0, zone DMA ... Node 0, zone DMA32 ... Node 0, zone Normal pages free 65812 min 14011 low 17513 high 21015 ... nr_free_pages 65812 nr_zone_inactive_anon 320181 nr_zone_active_anon 2581361 nr_zone_inactive_file 69760 nr_zone_active_file 82232 ... Node 0, zone Movable ... Node 0, zone Device ... // pages / min、low、high,就是上面提到的三个内存阈值 // pages / free 剩余内存页数,它与后面的 nr_free_pages 相同 // nr_zone_active_anon / nr_zone_inactive_anon,分别是活跃和非活跃的匿名页数 // nr_zone_active_file 和 nr_zone_inactive_file,分别是活跃和非活跃的文件页数 // 此时,free > high,不会运行 kswapd0 回收内存 # cat /proc/zoneinfo | grep -A 10 'Node 0, zone Normal'
另外,某个 Node 内存不足时,操作系统可以从其他 Node 寻找空闲内存,也可以从本地内存中回收内存。具体选哪种模式可以通过 /proc/sys/vm/zone_reclaim_mode 来调整:
1)echo 0 > /proc/sys/vm/zone_reclaim_mode,默认模式,既可以从其他 Node 寻找空闲内存,也可以从本地回收内存;
2)echo 1 > /proc/sys/vm/zone_reclaim_mode,只回收本地内存,
3)echo 2 > /proc/sys/vm/zone_reclaim_mode,只回收本地内存,可以回写脏数据回收内存;
4)echo 4 > /proc/sys/vm/zone_reclaim_mode,只回收本地内存,以用 Swap 方式回收内存;
swappiness
Linux Kernel 提供 /proc/sys/vm/swappiness 选项,用来调整使用 Swap 的积极程度。
swappiness 的范围是 0-100
1)数值越大,越积极使用 Swap,也就是更倾向于回收匿名页;
2)数值越小,越消极使用 Swap,也就是更倾向于回收文件页;
swappiness 显示的是默认值 60,这是一个相对中和的配置,所以系统会根据实际运行情况,选择合适的回收类型,比如回收不活跃的匿名页,或者不活跃的文件页。
注意,这并不是内存的百分比,而是调整 Swap 积极程度的权重。即使设置成 0,当 pages_free + File-backed pages < pages_high 时,还是会发生 Swap
分析正在使用 SWAP 的进程
在 /proc/<pid>/status 中,VmSwap,用来查看进程 Swap 换出的虚拟内存大小:
for file in /proc/*/status do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file done | sort -k 3 -n -r | head
问题排查:定位和分析 SWAP 使用升高
# sar -r -S 1 // 隔1秒输出一组数据,-r 表示显示内存使用情况,-S 表示显示 Swap 使用情况 // sar 的输出结果是两个表格,第一个表格表示内存的使用情况,第二个表格表示 Swap 的使用情况,前面的 kb 前缀表示单位 // kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需要内存的估计值。 // %commit,就是 kbcommit 相对总内存的百分比 // kbactive,表示活跃内存,也就是最近使用过的内存,一般不会被系统回收 // kbinact,表示非活跃内存,也就是不常访问的内存,有可能会被系统回收 # dd if=/dev/sdb of=/dev/null bs=1G count=2048 // 观察命令 sar 输出变化 // 可以清楚地看到,总的内存使用率(%memused)在不断增长,从开始的 80% 一直长到了 84%,并且主要内存都被缓冲区(kbbuffers)占用: // 开始时,剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,由此可知,剩余内存不断分配给了缓冲区。 // 渐渐地,总的内存使用率(%memused)开始下降,Swap 的使用开始逐渐增大 // 稳定后,总的内存使用率(%memused)保持在固定范围,62%, # cachetop // 使用 cachetop 命令将看到 dd 命令的缓存命中率,这也是 buffer 升高的直接原因 # watch -d grep -A 15 'Normal' /proc/zoneinfo // 可以发现: // 剩余内存(pages_free)在一个小范围内不停地波动,当它小于页低阈值(pages_low) 时,又会突然增大到一个大于页高阈值(pages_high)的值
当剩余内存小于页低阈值(pages_low)时,系统会回收一些缓存和匿名内存,使剩余内存增大。缓存回收导致缓冲区减小,匿名内存的回收导致Swap 的使用增大
由于 dd 还在继续剩余内存又会重新分配给缓存,导致剩余内存减少,缓冲区增大。
如果多次运行 dd 和 sar 命令,可能会发现,在多次的循环重复中,有时候是 Swap 用得比较多,有时候 Swap 很少,反而缓冲区的波动更大。换句话说,系统回收内存时,有时候会回收更多的文件页,有时候又回收了更多的匿名页,而 swappiness 正是调整不同类型内存回收的配置选项。
问题总结
SWAP 使用升高,原因:匿名内存回收,并被写入 SWAP 分区
1)通过 free,发现大部分内存都被缓存占用
2)使用 vmstat 或者 sar 观察一下缓存的变化趋势,确认缓存的使用是否还在继续增大
3)用缓存 / 缓冲区分析工具(比如 cachetop、slabtop 等),分析这些缓存到底被哪里占用
参考文献