「Linux」- SWAP(学习笔记)

  CREATED BY JENKINSBOT

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 分区

=> 匿名内存回收,原因:内存资源紧张,应该观察内存使用情况
=> 观察内存使用情况:使用 sar 命令,关注 used、free、buffer、cache

1)通过 free,发现大部分内存都被缓存占用
2)使用 vmstat 或者 sar 观察一下缓存的变化趋势,确认缓存的使用是否还在继续增大
3)用缓存 / 缓冲区分析工具(比如 cachetop、slabtop 等),分析这些缓存到底被哪里占用

参考文献

19 | 案例篇:为什么系统的Swap变高了(上)