16 | 系统的Swap变高了

1. 概述

内存泄漏会导致系统内存资源逐渐耗尽,影响系统稳定性。当内存资源紧张时,系统通常有两种应对方式:

  1. OOM(Out Of Memory)杀死进程
    系统会杀死占用大量内存的进程,释放内存资源以供其他进程使用。这种机制简单直接,在前面的内容中已有详细介绍。
  2. 内存回收机制
    系统会优先释放可回收的内存资源,如文件页(File-backed Page)。
    • 未修改的文件页:可以直接释放,需要时从磁盘重新加载。
    • 脏页(Dirty Page) :需要先写入磁盘,然后才能释放。写入磁盘的方式有:
      • 应用程序通过 fsync 系统调用主动同步。
      • 内核线程 pdflush 自动刷新。
    • 文件映射页:通过内存映射的文件页也属于文件页,释放后可从文件重新加载。

此外,还有一种不可直接释放的内存------匿名页(Anonymous Page),即应用程序动态分配的堆内存。这类内存由于可能再次被访问,通常不能直接释放。但对于很少被访问的匿名页,系统可以通过 Swap 机制进行处理:

  • Swap 机制:将不常访问的内存写入磁盘,从而释放内存资源供其他进程使用。当这些内存再次被访问时,再从磁盘读入。

接下来深入探讨 Swap 的工作原理,以及如何优化 Swap 的使用。

1.1. Swap 原理

Swap 说白了就是把一块磁盘空间或者一个本地文件(以下讲解以磁盘为例),当成内存来使用。它包括换出和换入两个过程。

  • 所谓换出,就是把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存。
  • 而换入,则是在进程再次访问这些内存的时候,把它们从磁盘读到内存中来。

简单来说换出就是从内存到磁盘,换入就从磁盘到内存。这一点不能记混。所以换出产生的写I/O、相反换入产生的读I/O。

Linux 系统中,Swap 机制的目的是回收内存资源,确保系统能够正常运行。那么,系统在什么情况下需要回收内存,又如何衡量内存是否紧张呢?

需要回收内存的场景

  1. 直接内存回收
    当有新的大块内存分配请求,但剩余内存不足时,系统会回收一部分内存(如缓存)以满足新请求。
  2. 定期内存回收
    系统中有一个专门的内核线程 kswapd0,负责定期回收内存,避免内存持续紧张的情况。

衡量内存是否紧张

kswapd0 使用内存水位(watermark)来评估系统的内存状态,具体包括以下三个阈值:

  • pages_min(最小水位) :表示系统能够正常运行所需的最低内存。
  • pages_low(低水位) :低于此水位时,kswapd0 开始回收内存。
  • pages_high(高水位) :高于此水位时,系统无需回收内存,表示内存充足。

系统剩余的空闲内存用 pages_free 表示。当 pages_free 接近或低于这些阈值时,系统会启动相应的内存回收机制以缓解压力。

在 Linux 系统中,kswapd0 不仅定期扫描内存的使用情况,还会根据剩余内存相对于三个水位阈值的位置,决定是否执行内存回收操作,以及回收的力度。以下是具体的内存状态判断和处理逻辑:

内存状态及处理逻辑

  1. 剩余内存 < 页最小阈值(pages_min)
    • 状态:进程可用内存耗尽,只有内核可以分配内存。
    • 处理:系统内存已极度紧张,必须通过强制回收释放内存资源。
  1. 页最小阈值 ≤ 剩余内存 < 页低阈值(pages_low)
    • 状态:内存压力较大,剩余内存不足。
    • 处理:kswapd0 会主动执行内存回收操作,直到剩余内存超过页高阈值为止。
  1. 页低阈值 ≤ 剩余内存 < 页高阈值(pages_high)
    • 状态:内存有一定压力,但还能满足新内存分配请求。
    • 处理:系统暂时不会触发内存回收。
  1. 剩余内存 ≥ 页高阈值(pages_high)
    • 状态:剩余内存充足,没有任何内存压力。
    • 处理:kswapd0 不需要执行回收操作,系统运行正常。

当剩余内存小于 页低阈值(pages_low) 时,系统会触发内存回收操作。这个 页低阈值 的设置与 页最小阈值(pages_min) 有关,而 页最小阈值 可以通过内核选项 /proc/sys/vm/min_free_kbytes 来间接配置。

计算公式

其他两个阈值是根据 页最小阈值 按以下公式计算得出的:

  • 页低阈值(pages_low) = 页最小阈值(pages_min) × 5 / 4
  • 页高阈值(pages_high) = 页最小阈值(pages_min) × 3 / 2

通过调整 min_free_kbytes 的值,可以间接影响这三个阈值,从而改变系统内存回收的行为和效率。

1.2. Swap与NUMA

在分析系统内存使用时,可能会遇到一种情况:尽管系统显示剩余内存很多,但 Swap 却仍在升高。这种现象通常与处理器的 NUMA(Non-Uniform Memory Access)架构有关。

什么是 NUMA?

在 NUMA 架构下,多个处理器被划分到不同的 Node,每个 Node 拥有独立的本地内存空间。同一个 Node 内部的内存空间还可以进一步划分为多个内存域(Zone),如:

  • DMA(直接内存访问区)
  • NORMAL(普通内存区)
  • MOVABLE(伪内存区)

尽管这些内存域的具体含义暂时不必深究,但在分析内存使用时,我们需要针对每个 Node 的内存空间分别进行检查。

NUMA 内存使用情况查看

使用 numactl 命令可以查看处理器在不同 Node 上的分布情况,以及每个 Node 的内存使用状态。例如:

yaml 复制代码
$ numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1
node 0 size: 7977 MB
node 0 free: 4416 MB

NUMA 架构与 Swap 的关系

了解了 NUMA 的架构和查看方法后,我们来探讨它与 Swap 的关系。NUMA 中的内存回收和阈值监控,直接影响了 Swap 的触发机制。

查看内存域的阈值

NUMA 架构下,每个内存域的三个阈值(页最小阈值页低阈值页高阈值 )可以通过 /proc/zoneinfo 文件查看。例如:

shell 复制代码
$ cat /proc/zoneinfo
...
Node 0, zone   Normal
  pages free     227894
        min      14896
        low      18620
        high     22344
...
      nr_free_pages 227894
      nr_zone_inactive_anon 11082
      nr_zone_active_anon 14024
      nr_zone_inactive_file 539024
      nr_zone_active_file 923986
...

关键指标说明

  • min、low、high:分别对应页最小阈值、页低阈值和页高阈值。
  • pages free :剩余内存页数,与 nr_free_pages 相同。
  • nr_zone_active_anonnr_zone_inactive_anon:活跃和非活跃的匿名页数。
  • nr_zone_active_filenr_zone_inactive_file:活跃和非活跃的文件页数。

从示例中可以看到,剩余内存(pages free )远大于页高阈值,因此此时的 kswapd0 不会触发内存回收。

Node 内存不足时的策略

当某个 Node 内存不足时,系统会有两种策略:

  1. 从其他 Node 寻找空闲内存。
  2. 从本地 Node 回收内存。

具体选择的策略可以通过 /proc/sys/vm/zone_reclaim_mode 调整,该选项支持以下值:

  • 0(默认值) :允许从其他 Node 寻找空闲内存,同时也可以回收本地内存。
  • 1:仅回收本地内存。
  • 2:允许回写脏数据来回收本地内存。
  • 4:允许使用 Swap 回收本地内存。

在内存回收机制中,Linux 会同时回收文件页和匿名页。

  • 文件页的回收:直接回收缓存,或将脏页写回磁盘后再释放内存。
  • 匿名页的回收:通过 Swap 机制,将其写入磁盘后释放内存。

那么,在实际回收内存时,系统优先回收哪一种呢?这取决于 /proc/sys/vm/swappiness 的配置。

关于 swappiness

  • 作用:调整使用 Swap 的积极程度。
  • 取值范围:0-100。
    • 数值越大,越倾向于回收匿名页(积极使用 Swap)。
    • 数值越小,越倾向于回收文件页(消极使用 Swap)。

需要注意,即使将 swappiness 设置为 0,当系统剩余内存和文件页小于页高阈值时,仍可能触发 Swap。

那么,当 Swap 使用升高时,要如何定位和分析呢?下面,我们就来看一个磁盘 I/O 的案例,实战分析和演练。

2. 案例

环境准备:

  • 操作系统:Ubuntu 18.04
  • 机器配置:2 CPU,8GB 内存
  • 你需要预先安装 sysstat 等工具,如 apt install sysstat

终端 1:

vbnet 复制代码
$ free
             total        used        free      shared  buff/cache   available
Mem:        8169348      331668     6715972         696     1121708     7522896
Swap:             0           0           0

如果已经使用了swap,可以通过关闭Swap在重新开启的方式清空Swap

css 复制代码
swapoff -a && swapon -a

若还没有配置Swap,参考如下命令

shell 复制代码
# 创建Swap文件
$ fallocate -l 8G /mnt/swapfile
# 修改权限只有根用户可以访问
$ chmod 600 /mnt/swapfile
# 配置Swap文件
$ mkswap /mnt/swapfile
# 开启Swap
$ swapon /mnt/swapfile

执行dd命令,模拟大文件读取

javascript 复制代码
# 写入空设备,实际上只有磁盘的读请求
$ dd if=/dev/sda3 of=/dev/null bs=1G count=2048

终端 2

makefile 复制代码
# 间隔1秒输出一组数据
# -r表示显示内存使用情况,-S表示显示Swap使用情况
$ sar -r -S 1
04:39:56    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
04:39:57      6249676   6839824   1919632     23.50    740512     67316   1691736     10.22    815156    841868         4

04:39:56    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:39:57      8388604         0      0.00         0      0.00

04:39:57    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
04:39:58      6184472   6807064   1984836     24.30    772768     67380   1691736     10.22    847932    874224        20

04:39:57    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:39:58      8388604         0      0.00         0      0.00

...


04:44:06    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
04:44:07       152780   6525716   8016528     98.13   6530440     51316   1691736     10.22    867124   6869332         0

04:44:06    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:44:07      8384508      4096      0.05        52      1.27

新指标介绍:

  • kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需要内存的估计值。%commit,就是这个值相对总内存的百分比。
  • kbactive,表示活跃内存,也就是最近使用过的内存,一般不会被系统回收。
  • kbinact,表示非活跃内存,也就是不常访问的内存,有可能会被系统回收。

以下是这些内存和 Swap 使用指标的表格形式:

指标 描述 示例值
kbmemfree 当前空闲的物理内存量(KB) 111648
kbavail 可用内存量(包括空闲内存和可回收的缓存内存,KB) 999228
kbmemused 已使用的物理内存量(KB) 7958468
%memused 物理内存使用百分比 98.62%
kbbuffers 缓存的内存量(KB) 1035896
kbcached 用于缓存文件数据的内存量(KB) 45508
kbcommit 已提交的内存量(KB) 591116
%commit 已提交内存占总内存的百分比 4.82%
kbactive 活跃的内存量(频繁访问的数据,KB) 33468
kbinact 不活跃的内存量(暂不访问的数据,KB) 1084000
kbdirty 被修改但尚未写入磁盘的内存量(脏页,KB) 12
kbswpfree 空闲的 Swap 空间量(KB) 4132596
kbswpused 已使用的 Swap 空间量(KB) 61704
%swpused Swap 空间使用百分比 1.47%
kbswpcad 被压缩的 Swap 内存量(KB) 15356
%swpcad 被压缩的 Swap 内存占总 Swap 空间的百分比 24.89%
  • 刚开始,剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,由此可知,剩余内存不断分配给了缓冲区。
  • 一段时间后,剩余内存已经很小,而缓冲区占用了大部分内存。这时候,Swap 的使用开始逐渐增大,缓冲区和剩余内存则只在小范围内波动。

转换成表格,看的更直观一点:

指标 04:39:56 PM 04:39:57 PM 04:39:58 PM 04:44:06 PM 变化趋势
kbmemfree 6249676 6249676 6184472 152780 显著下降
kbavail 6839824 6839824 6807064 6525716 稍微下降
kbmemused 1919632 1919632 1984836 8016528 显著上升
%memused 23.50% 23.50% 24.30% 98.13% 显著上升
kbbuffers 740512 740512 772768 6530440 显著上升
kbcached 67316 67316 67380 51316 稍微下降
kbcommit 1691736 1691736 1691736 1691736 稳定
%commit 10.22% 10.22% 10.22% 10.22% 稳定
kbactive 815156 815156 847932 867124 稍微上升
kbinact 841868 841868 874224 6869332 显著上升
kbdirty 40 40 200 0 显著下降
kbswpfree 8388604 8388604 8388604 8388604 稳定
kbswpused 0 0 0 0 稳定
%swpused 0.00% 0.00% 0.00% 0.00% 稳定
kbswpcad 0 0 0 0 稳定
%swpcad 0.00% 0.00% 0.00% 0.00% 稳定

buffers升高可以通过cachetop命令查看

erlang 复制代码
$ cachetop 5
12:28:28 Buffers MB: 6349 / Cached MB: 87 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
   18280 root     python                 22        0        0     100.0%       0.0%
   18279 root     dd                  41088    41022        0      50.0%      50.0%

通过 cachetop 的输出,我们得出以下结论和分析:

  1. 命中率与未命中缓存页
    • dd 进程的缓存命中率:50%。
    • 未命中缓存页数(MISSES) :41022 页。
    • 这表明,运行的 dd 命令导致了大量缓冲区的占用,从而引发缓冲区使用升高。
  1. 缓冲区与 Swap 的关系
    • 缓冲区占用了系统绝大部分内存,属于可回收内存。
    • 按照直观理解,当内存不足时,系统应优先回收缓冲区,而不是增加 Swap 的使用。
  1. 进一步分析的必要性
    • 为了理解 Swap 使用升高的原因,需要通过 /proc/zoneinfo 查看:
      • 剩余内存(pages_free)
      • 内存阈值(pages_min、pages_low、pages_high)
      • 匿名页(活跃和非活跃)
      • 文件页(活跃和非活跃)
css 复制代码
# -d 表示高亮变化的字段
# -A 表示仅显示Normal行以及之后的15行输出
$ watch -d grep -A 15 'Normal' /proc/zoneinfo
Node 0, zone   Normal
  pages free     21328
        min      14896
        low      18620
        high     22344
        spanned  1835008
        present  1835008
        managed  1796710
        protection: (0, 0, 0, 0, 0)
      nr_free_pages 21328
      nr_zone_inactive_anon 79776
      nr_zone_active_anon 206854
      nr_zone_inactive_file 918561
      nr_zone_active_file 496695
      nr_zone_unevictable 2251
      nr_zone_write_pending 0

通过分析剩余内存(pages_free)的波动变化,可以发现以下规律:

  1. 内存波动原因
    • 当剩余内存小于页低阈值(pages_low)时,系统会启动内存回收机制,释放一些缓存和匿名内存,使剩余内存迅速增加到高于页高阈值(pages_high)。
    • 回收过程中:
      • 缓存的回收导致缓冲区(buffers)减少。
      • 匿名内存的回收导致 Swap 的使用量增加。
  1. 循环变化
    • 在内存回收后,由于新的写入操作(如 dd 命令),剩余内存会被重新分配给缓存,导致剩余内存再次减少,缓冲区增大。
    • 这一过程会循环往复,形成一个动态平衡。
  1. Swap 和缓冲区的波动
    • 在多次运行 dd 和观察 sar 数据时,有时会发现 Swap 使用较多,而缓冲区波动较小;有时则相反。
    • 这表明系统在回收内存时,对回收文件页和匿名页的倾向不明显。
  1. 影响因素
    • 系统内存回收策略受 swappiness 参数的影响。该参数可以调整文件页和匿名页回收的优先级,从而影响内存波动的具体表现。

显然,当前的Swap使用比较激进,这跟swappiness有关

ruby 复制代码
root@calvin:~# cat /proc/sys/vm/swappiness
60

那么如何知道Swap换入换出的是哪些程序呢?建议还是通过/proc文件系统排查。

shell 复制代码
# 按VmSwap使用量对进程排序,输出进程名称、进程ID以及SWAP用量
$ for file in /proc/*/status ; do awk '/VmSwap|Name|^Pid/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 3 -n -r | head
dockerd 2226 10728 kB
docker-containe 2251 8516 kB
snapd 936 4020 kB
networkd-dispat 911 836 kB
polkitd 1004 44 kB

proc文件系统的好处是通用的,任何操作系统都可以使用,若有条件,还有一个更简单的工具,smem

bash 复制代码
smem --sort swap

Ubuntu安装如下

bash 复制代码
apt update
apt-get install smem -y

#安装不成功建议源码安装,地址如下:
https://www.selenic.com/smem/download/
tar xvf smem-1.4.tar.gz
cp smem /usr/local/bin/

3. 小结

在内存资源紧张的情况下,Linux 会通过 Swap 将不常访问的匿名页换出到磁盘,以释放内存。当需要访问这些页时,再从磁盘换入到内存。以下是相关配置和方法的总结:

关键配置项:

  1. 调整内存回收阈值
    • /proc/sys/vm/min_free_kbytes:设置内存回收的最低阈值,控制系统定期回收内存的行为。
  1. 调整内存回收倾向
    • /proc/sys/vm/swappiness:调整文件页和匿名页的回收倾向。值越低,倾向于回收文件页;值越高,倾向于回收匿名页。

定位 Swap 升高的原因:

  1. 使用工具和文件
    • sar:查看系统整体的内存和 Swap 使用变化。
    • /proc/zoneinfo:观察内存剩余情况和内存域的活跃信息。
    • /proc/pid/status:分析具体进程的内存使用情况。
  1. 目标:通过这些工具找出 Swap 升高的根源,以及受影响的进程。

降低 Swap 使用的方法:

  1. 禁用 Swap
    • 如果服务器内存充足,可以禁用 Swap,避免不必要的内存换出。
    • 许多云平台默认禁用 Swap。
  1. 降低 swappiness 值
    • 适当降低 swappiness,减少系统内存回收时对 Swap 的使用倾向。
  1. 锁定关键应用的内存
    • 对于响应延迟敏感的应用,可以使用库函数 mlock() mlockall() 锁定其内存,防止被换出。

合理配置 min_free_kbytesswappiness,以及针对性地分析和优化 Swap 使用,不仅能降低 Swap 的使用,还能提高系统整体性能。

相关推荐
迪小莫学AI38 分钟前
【小白向超详细】使用 VSCode 远程连接 Linux 服务器详细教程
linux·服务器·vscode
等一场春雨1 小时前
linux 查找redis 的配置文件 (`redis.conf`)
linux·运维·redis
ltwoxc1 小时前
04-Linux系统编程之进程
linux
hope_wisdom1 小时前
Linux系统编程之多线程
linux·多线程·pthread·linux编程
蒲公英的孩子2 小时前
DCU异构程序——Bank冲突
linux·分布式·算法·架构
xxxx1234452 小时前
Linux-Ubuntu之RTC实时时钟显示
linux·c语言·ubuntu
三天不学习2 小时前
【linux系统】mysql 数据库迁移至新服务器
linux·服务器·数据库
北顾南栀倾寒2 小时前
[Linux]进程间通信-共享内存与消息队列
linux·运维·服务器
冬天vs不冷2 小时前
Shell编程详解
linux·ssh
cuckooman2 小时前
Ubuntu无法创建python venv环境
linux·python·ubuntu