19-案例篇:为什么系统的Swap变高了?(上)
为什么要有swap
当发生了内存泄漏时,或者运行了大内存的应用程序,导致系统的内存资源紧张时,系统又会如何应对呢?
- OOM杀死进程
- 内存回收
OOM 杀死进程
内存资源紧张导致的OOM(Out Of Memory),指的是系统杀死占用大量内存的进程,释放这些内存,再分配给其他更需要的进程
内存回收
内存回收也就是系统释放掉可以回收的内存,
比如缓存和缓冲区,就属于可回收内存,它们在内存管理中,通常被叫做文件页(File-backed Page)
大部分文件页,都可以直接回收,以后有需要时,再从磁盘重新读取就可以了
而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页)
就得先写入磁盘,然后才能进行内存释放
这些脏页,一般可以通过两种方式写入磁盘
- 可以在应用程序中,通过系统调用fsync ,把脏页同步到磁盘中
- 交给系统由内核线程pdflush负责这些脏页的刷新
除了缓存和缓冲区,通过内存映射获取的文件映射页,也是一种常见的文件页
它也可以被释放掉,下次再访问的时候,从文件重新读取
应用程序动态分配的堆内存,也就是匿名页(Anonymous Page),不能直接回收
那这些内存在分配后很少被访问,似乎也是一种资源浪费
Linux的Swap机制,会把这些不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用
再次访问这些内存时,重新从磁盘读入内存就可以了。
swap原理
Swap就是把一块磁盘空间或者一个本地文件,当成内存来使用
它包括换出和换入两个过程
- 换出,就是把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存
- 换入,则是在进程再次访问这些内存的时候,把它们从磁盘读到内存中来
既然Swap是为了回收内存,那么Linux到底在什么时候需要回收内存呢?又该怎么来衡量内存是不是紧张呢?
- 直接内存回收: 有新的大块内存分配请求,但是剩余内存不足
这个时候系统就需要回收一部分内存(比如缓存)进而尽可能地满足新内存请求
这个过程通常被称为直接内存回收 - kswapd0: 除了直接内存回收,还有一个专门的内核线程用来定期回收内存
为了衡量内存的使用情况,kswapd0 定义了三个内存阈值(watermark也称为水位)分别是- 页最小阈值(pages_min)
- 页低阈值(pages_low)
- 页高阈值(pages_high)
剩余内存,则使用pages_free表示
kswapd0定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作
- 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存
- 剩余内存落在页最小阈值和页低阈值中间,说明内存压力比较大,剩余内存不多了
这时kswapd0会执行内存回收,直到剩余内存大于高阈值为止 - 剩余内存落在页低阈值和页高阈值中间,说明内存有一定压力,但还可以满足新内存请求
- 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力
一旦剩余内存小于页低阈值,就会触发内存的回收
这个页低阈值,其实可以通过内核选项/proc/sys/vm/min_free_kbytes来间接设置
[root@local_sa_192-168-1-6 ~]# cat /proc/sys/vm/min_free_kbytes
67584
min_free_kbytes设置了页最小阈值,而其他两个阈值,都是根据页最小阈值计算生成的,计算方法如下
pages_low = pages_min * 5 / 4
pages_high = pages_min * 3 / 2
numa与swap
很多情况下,明明发现了Swap升高,可是在分析系统的内存使用时,却很可能发现, 系统剩余内存还多着呢
为什么剩余内存很多的情况下,也会发生Swap呢?
这正是处理器的NUMA (Non-Uniform Memory Access)架构导致的
在NUMA架构下,多个处理器被划分到不同Node上,且每个Node都拥有自己的本地内存空间
同一个Node内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),
比如直接内存访问区(DMA)、普通内存区(NORMAL)、伪内存区(MOVABLE)等, 如下图所示
先不用特别关注这些内存域的具体含义,只要会查看阈值的配置,以及缓存、匿名页的实际使用情况就够了。
既然NUMA架构下的每个Node都有自己的本地内存空间,那么,在分析内存的使用时,也应该针对每个Node单独分析
可以通过numactl命令,来查看处理器在Node的分布情况,以及每个Node的内存使用情况
# numactl输出的示例
# 安装 yum install numactl -y
[root@local_sa_192-168-1-6 ~]# numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1
node 0 size: 3933 MB
node 0 free: 1458 MB
node distances:
node 0
0: 10
#
这个界面显示,系统中只有一个Node,也就是Node 0 ,而且编号为0和1的两个CPU, 都位于Node 0上
另外,Node 0的内存大小为3933MB,剩余内存为1458MB
[root@local_sa_192-168-1-6 ~]# free -m
total used free shared buff/cache available
Mem: 3933 385 1458 29 2089 3273
Swap: 1535 0 1535
了解了NUNA的架构和NUMA内存的查看方法后,可能就要问了这跟Swap有什么 关系呢?
实际上,前面提到的三个内存阈值(页最小阈值、页低阈值和页高阈值)
都可以通过内存域在proc文件系统中的接口/proc/zoneinfo来查看
[root@local_sa_192-168-1-6 ~]# cat /proc/zoneinfo
...
Node 0, zone Normal
pages free 2371
min 1905
low 2381
high 2857
...
nr_free_pages 2371
nr_zone_inactive_anon 32225
nr_zone_active_anon 62
nr_zone_inactive_file 35222
nr_zone_active_file 10224
...
#
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会回收内存
当某个Node内存不足时,系统可以从其他Node寻找空闲内存,也可以从本地内存中回收内存
具体选哪种模式,可以通过/proc/sys/vm/zone_reclaim_mode来调整
它支持以下几个选项:
- 默认的0 ,也就是刚刚提到的模式,表示既可以从其他Node寻找空闲内存,也可以从本地回收内存
- 1、2、4 都表示只回收本地内存,2表示可以回写脏数据回收内存,4表示可以用Swap方式回收内存
swappiness
内存回收的机制,这些回收的内存既包括了文件页,又包括了匿名页
- 对文件页的回收,当然就是直接回收缓存,或者把脏页写回磁盘后再回收
- 而对匿名页的回收,其实就是通过Swap机制,把它们写入磁盘后再释放内存
既然有两种不同的内存回收机制,那么在实际回收内存时, 到底该先回收哪一种呢?
Linux提供了一个/proc/sys/vm/swappiness选项,用来调整使用Swap的积极程度
swappiness的范围是0-100,数值越大,越积极使用Swap,也就是更倾向于回收匿名页
数值越小,越消极使用Swap,也就是更倾向于回收文件页
虽然swappiness的范围是0-100,不过要注意,这并不是内存的百分比
而是调整Swap积极程度的权重,即使你把它设置成 0
当剩余内存 + 文件页小于页高阈值时,还 是会发生Swap
小结
在内存资源紧张时,Linux通过直接内存回收和定期扫描的方式,来释放文件页和匿名页,以便把内存分配给更需要的进程使用
- 文件页的回收比较容易理解,直接清空,或者把脏数据写回磁盘后再释放
- 匿名页的回收,需要通过Swap换出到磁盘中,下次访问时,再从磁盘换入到内存中
可以设置 /proc/sys/vm/min_free_kbytes,来调整系统定期回收内存的阈值(也就是页低阈值)
还可以设置 /proc/sys/vm/swappiness,来调整文件页和匿名页的回收倾向
在NUMA架构下,每个Node都有自己的本地内存空间,而当本地内存不足时,默认既可以从其他Node寻找空闲内存,也可以从本地内存回收。
可以设置 /proc/sys/vm/zone_reclaim_mode ,来调整 NUMA 本地内存的回收策略