【linux性能优化】系统Swap变高原因分析

一、内存处理

1.1 内存资源紧张的应对

当发生了内存泄漏或者运行大内存的应用程序,导致系统的内存资源紧张时,系统又会如何应对呢?
这其实会导致两种可能结果,内存回收和OOM杀死进程

  • OOM杀死进程

内存资源紧张导致的OOM(Out Of Memory)
指的是系统杀死占用大量内存的进程,释放这些内存,再分配给其他更需要的进程

  • 内存回收

内存回收就是系统释放掉可以回收的内存
比如缓存和缓冲区就属于可回收内存,它们在内存管理中,通常被叫做文件页(File-backed Page)

1.2 脏页

大部分文件页都可以直接回收,以后有需要时再从磁盘重新读取就可以了
而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘才能进行内存释放

这些脏页,一般可以通过两种方式写入磁盘:

  • 可以在应用程序中,通过系统调用fsync把脏页同步到磁盘中
  • 可以交给系统,由内核线程pdflush负责这些脏页的刷新

除了缓存和缓冲区,通过内存映射获取的文件映射页也是一种常见的文件页,它可以被释放掉,下次再访问时从文件重新读取

1.3 匿名页

除了文件页外,还有没有其他的内存可以回收呢?比如,应用程序动态分配的堆内存,也就是在内存管理中说到的匿名页(Anonymous Page),是不是也可以回收呢?

它们很可能还要再次被访问,当然不能直接回收呀。确实这些内存自然不能直接释放

但是,如果这些内存在分配后很少被访问,似乎也是一种资源浪费,是不是可以把它们暂时存在磁盘里,释放内存给其他更需要的进程?

其实,这正是Linux的Swap机制
Swap把这些不常访问的内存先写到磁盘中,然后释放这些内存给其他更需要的进程使用,再次访问这些内存时,重新从磁盘读入内存即可

二、Swap原理

先来看看,Swap究竟是怎么工作的

2.1 Swap定义

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

  • 换出

把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存

  • 换入

在进程再次访问这些内存的时候,把它们从磁盘读到内存中来

所以Swap其实是把系统的可用内存变大
这样即使服务器的内存不足,也可以运行大内存的应用程序

2.2 Swap的作用

事实上,内存再大对应用程序来说,也有不够用的时候

  • 延缓OOM时间

一个很典型的场景就是,即使内存不足时,有些应用程序也并不想被OOM杀死,而是希望能缓一段时间等待人工介入,或者等系统自动释放其他进程的内存再分配给它

  • 电脑休眠和快速开机

常见的笔记本电脑的休眠和快速开机的功能也基于Swap
休眠时,把系统的内存存入磁盘,这样等到再次开机时只要从磁盘中加载内存就可以,省去了很多应用程序的初始化过程,加快了开机速度

2.3 如何衡量内存是否紧张

话说回来,既然Swap是为了回收内存,那么Linux到底在什么时候需要回收内存呢?一直在说内存资源紧张,又该怎么来衡量内存是不是紧张呢?

  1. 直接内存回收

一个最容易想到的场景就是,有新的大块内存分配请求但是剩余内存不足,这个时候系统就需要回收一部分内存(比如缓存),进而尽可能地满足新内存请求。这个过程通常被称为直接内存回收

  1. 定期回收内存

除了直接内存回收,还有一个专门的内核线程用来定期回收内存,也就是kswapd0
为了衡量内存的使用情况,kswapd0定义了三个内存阈值(watermark,也称为水位),分别是:

  • 页最小阈值(pages_min)
  • 页低阈值(pages_low)
  • 页高阈值(pages_high)

剩余内存则使用pages_free表示

下面这张图表示它们的关系:
在这里插入图片描述
kswapd0定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作

  • 剩余内存小于页最小阈值

说明进程可用内存都耗尽了,只有内核才可以分配内存

  • 剩余内存落在页最小阈值和页低阈值中间

说明内存压力比较大且剩余内存不多,这时kswapd0会执行内存回收,直到剩余内存大于高阈值为止

  • 剩余内存落在页低阈值和页高阈值中间

说明内存有一定压力,但还可以满足新内存请求

  • 剩余内存大于页高阈值

说明剩余内存比较多,没有内存压力

可以看到,一旦剩余内存小于页低阈值,就会触发内存的回收

这个页低阈值,其实可以通过内核选项/proc/sys/vm/min_free_kbytes来间接设置
min_free_kbytes设置了页最小阈值,而其他两个阈值,都是根据页最小阈值计算生成的,计算方法如下 :

pages_low = pages_min*5/4
pages_high = pages_min*3/2

三、NUMA与Swap

很多情况下,明明发现Swap升高,可是在分析系统的内存使用时却发现,系统剩余内存还很多。那为什么剩余内存很多的情况下,也会发生Swap呢?

3.1 NUMA架构

这是由于处理器的NUMA(Non-Uniform Memory Access)架构导致

NUMA架构下,多个处理器被划分到不同Node上,且每个Node都拥有自己的本地内存空间

而同一个Node内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),比如

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

如下图所示:

在这里插入图片描述
既然NUMA架构下的每个Node都有自己的本地内存空间,那么在分析内存的使用时,也应该针对每个Node单独分析

3.2 numactl查看Node内存使用

可以通过numactl命令查看处理器在Node的分布情况,以及每个Node的内存使用情况,比如:

$ numactl ‑‑hardware
available: 1 nodes (0)
node 0 cpus: 0 1
node 0 size: 7977 MB
node 0 free: 4416 MB
...

这个界面显示这个系统中只有一个Node0,而且编号为0和1的两个CPU都位于Node0上,另外Node0的内存大小为7977MB,剩余内存为4416MB

3.3 NUMA和Swap的关系

了解了NUNA的架构和NUMA内存的查看方法后,但是这跟Swap有什么关系呢?

实际上,三个内存阈值(页最小阈值、页低阈值和页高阈值)都可以通过内存域在proc文件系统中的接口/proc/zoneinfo查看,比如:

$ 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
...

这个输出中有大量指标,先看看几个比较重要的

  • pages

min、low、high分别是对应的三个内存阈值,而free是剩余内存页数(与nr_free_pages相同)

  • nr_zone_active_anon

活跃的匿名页数

  • nr_zone_inactive_anon

非活跃的匿名页数

  • nr_zone_active_file

活跃的文件页数

  • nr_zone_inactive_file

非活跃的文件页数

从这个输出结果可以发现,剩余内存远大于页高阈值,所以此时的kswapd0不会回收内存

3.4 Node内存不足的处理

当然,某个Node内存不足时,系统可以从其他Node寻找空闲内存,也可以从本地内存中回收内存
具体选哪种模式,可以通过/proc/sys/vm/zone_reclaim_mode来调整,支持以下几个选项:

  • 默认0

就是刚刚提到的模式,表示既可以从其他Node寻找空闲内存,也可以从本地回收内存

  • 1、2、4

都表示只回收本地内存,2表示可以回写脏数据回收内存,4表示可以用Swap方式回收内存

四、swappiness

4.1 回收的内存

现在基本理解内存回收的机制,这些回收的内存既包括了文件页,又包括了匿名页

  • 对文件页的回收

直接回收缓存,或者把脏页写回磁盘后再回收

  • 对匿名页的回收

其实就是通过Swap机制,把它们写入磁盘后再释放内存

4.2 内存回收机制的选择

不过,既然有两种不同的内存回收机制,那么在实际回收内存时到底该先回收哪一种呢?

其实,Linux提供了一个/proc/sys/vm/swappiness选项,用来调整使用Swap的积极程度

swappiness的范围是0-100,数值大小的含义:

  • 数值越大越积极使用Swap,也就是更倾向于回收匿名页
  • 数值越小越消极使用Swap,也就是更倾向于回收文件页

虽然swappiness的范围是 0-100,不过要注意这并不是内存的百分比,而是调整Swap积极程度的权重,即使把它设置成0,当剩余内存+文件页小于页高阈值时还是会发生Swap

五、案例

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

们打开两个终端,分别SSH登录到两台机器上

5.1 free查看Swap的使用情况

在终端中运行free命令,查看Swap的使用情况,比如:

$ free
             total        used        free      shared  buff/cache   available
Mem:        8169348      331668     6715972         696     1121708     7522896
Swap:             0           0           0

从这个free输出可以看到Swap的大小是0,这说明没有配置Swap

5.2 配置并开启Swap

为了继续Swap的案例需要先配置并开启Swap

要开启Swap,首先要清楚Linux本身支持两种类型的Swap,即Swap分区和Swap文件
以Swap文件为例,在第一个终端中运行下面的命令开启Swap:

# 创建Swap文件,配置Swap文件的大小为8GB
$ fallocate ‑l 8G /mnt/swapfile
# 修改权限只有根用户可以访问
$ chmod 600 /mnt/swapfile
# 配置Swap文件
$ mkswap /mnt/swapfile
# 开启Swap
$ swapon /mnt/swapfile

然后,再执行free命令,确认Swap配置成功:

$ free
             total        used        free      shared  buff/cache   available
Mem:        8169348      331668     6715972         696     1121708     7522896
Swap:       8388604           0     8388604

free的输出中,Swap空间以及剩余空间都从0变成了8GB,说明Swap已经正常开启

5.3 dd默认大文件读取

接下来,在第一个终端中运行下面的dd命令,模拟大文件的读取:

# 写入空设备,实际上只有磁盘的读请求
$ dd if=/dev/sda1 of=/dev/null bs=1G count=2048
5.4 sar观察内存指标

接着,在第二个终端中运行sar命令,查看内存各个指标的变化情况
需要多观察一会儿,便于查看这些指标的变化情况

# 间隔1秒输出一组数据
# ‑r表示显示内存使用情况,‑S表示显示Swap使用情况
$ sar ‑r ‑S 1
04:39:56    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive
04:39:57      6249676   6839824   1919632     23.50    740512     67316   1691736     10.22    815156
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
04:39:58      6184472   6807064   1984836     24.30    772768     67380   1691736     10.22    847932
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
04:44:07       152780   6525716   8016528     98.13   6530440     51316   1691736     10.22    867124
04:44:06    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:44:07      8384508      4096      0.05        52      1.27

可以看到sar的输出结果是两个表格

  • 第一个表格表示内存的使用情况
  • 第二个表格表示Swap的使用情况

其中,各个指标名称前面的kb前缀,表示这些指标的单位是KB

5.5 sar内存指标说明

去掉前缀后,发现大部分指标都已经见过了,剩下的几个新出现的指标:

  • kbcommit

表示当前系统负载需要的内存
它实际上是为了保证系统内存不溢出,对需要内存的估计值

  • %commit

这个值相对总内存的百分比

  • kbactive

表示活跃内存,也就是最近使用过的内存,一般不会被系统回收

  • kbinact

表示非活跃内存,也就是不常访问的内存,有可能会被系统回收

5.6 分析sar数据

清楚了界面指标的含义后,再结合具体数值分析相关的现象
可以清楚地看到,总的内存使用率(%memused)在不断增长,从开始的23%一直长到了98%,并且主要内存都被缓冲区(kbbuffers)占用,具体来说:

  • 刚开始

剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,由此可知,剩余内存不断分配给了缓冲区

  • 一段时间后

剩余内存已经很小,而缓冲区占用了大部分内存。这时候,Swap的使用开始逐渐增大,缓冲区和剩余内存则只在小范围内波动

5.7 cachetop分析进程缓存增大原因

那为什么缓冲区在不停增大?这又是哪些进程导致的呢?

显然,还得看看进程缓存的情况。cachetop正好能满足这一点

在第二个终端中,按下Ctrl+C停止sar命令,然后运行cachetop命令,观察缓存的使用情况:

$ 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的输出,可以看到dd进程的读写请求只有50%的命中率,并且未命中的缓存页数(MISSES)为41022(单位是页),这说明正是案例开始时运行的dd,导致了缓冲区使用升高

5.8 /proc/zoneinfo分析Swap升高原因

那为什么Swap也跟着升高了呢?
直观来说,缓冲区占了系统绝大部分内存,还属于可回收内存,内存不够用时不应该先回收缓冲区吗?

这种情况,还得进一步通过/proc/zoneinfo观察剩余内存、内存阈值以及匿名页和文件页的活跃情况

在第二个终端中,按下Ctrl+C停止cachetop命令,然后运行下面的命令观察/proc/zoneinfo中这几个指标的变化情况:

# ‑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)在一个小范围内不停地波动,当它小于页低阈值(pages_low)时,又会突然增大到一个大于页高阈值(pages_high)的值

5.9 现象分析结论

再结合刚刚用sar看到的剩余内存和缓冲区的变化情况,可以推导出剩余内存和缓冲区的波动变化,正是由于内存回收和缓存再次分配的循环往复

  • 当剩余内存小于页低阈值

系统会回收一些缓存和匿名内存,使剩余内存增大。其中,缓存的回收导致sar中的缓冲区减小,而匿名内存的回收导致了Swap的使用增大

  • dd持续进行

剩余内存又会重新分配给缓存,导致剩余内存减少,缓冲区增大

六、小结

在内存资源紧张时,Linux通过直接内存回收和定期扫描的方式来释放文件页和匿名页,下次访问的时候再从磁盘换入到内存中来,以便把内存分配给更需要的进程使用

  • 文件页的回收

直接清空或者把脏数据写回磁盘后再释放

  • 匿名页的回收

需要通过Swap换出到磁盘中,下次访问时再从磁盘换入到内存中

可以设置/proc/sys/vm/min_free_kbytes来调整系统定期回收内存的阈值,也可以设置/proc/sys/vm/swappiness来调整文件页和匿名页的回收倾向

在NUMA架构下,每个Node都有自己的本地内存空间,而当本地内存不足时默认既可以从其他Node寻找空闲内存,也可以从本地内存回收

可以设置/proc/sys/vm/zone_reclaim_mode,来调整NUMA本地内存的回收策略

当Swap变高时可以用sar/proc/zoneinfo/proc/pid/status等方法,查看系统和进程的内存使用情况,进而找出Swap升高的根源和受影响的进程

反过来说,通常降低Swap的使用可以提高系统的整体性能,那要怎么做呢?几种常见的降低方法:

  • 禁止Swap

现在服务器的内存足够大,所以除非有必要,禁用Swap就可以了
随着云计算的普及,大部分云平台中的虚拟机都默认禁止Swap

  • 降低swappiness的值

如果实在需要用到Swap,可以尝试降低swappiness的值,减少内存回收时Swap的使用倾向

  • 锁定内存

响应延迟敏感的应用,如果在开启Swap的服务器中运行可以用库函数mlock()或者mlockall()锁定内存,阻止它们的内存换出

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sysu_lluozh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值