目录
五、系统 swap 分区
情景:
1、当发生内存泄露时,或者运行了大内存的应用程序,导致系统的内存资源紧张时,系统要如何应对?
2、内存回收或 OOM 杀死进程
3、当内存资源紧张时,Linux 通过直接内存回收和定期扫描的方式,来释放文件页和匿名页,以便把内存分配给更需要的进程使用
5.1 内存回收和 OOM
- OOM 是指系统杀死占用大量内存的进程,释放这些内存,再分配给其他更需要的进程。
- 内存回收是系统释放掉可以回收的内存,比如缓存和缓冲区。在内存管理中,叫做文件页。
5.1.1 内存回收
- 文件页 —— 缓存和缓冲区
- 大部分文件页是可以直接回收,有需要时,再从磁盘重新读取好了。
- 被应用程序修改过,并且暂时还没写入磁盘的数据(脏页),得先写入磁盘,然后才能进行内存释放
- 匿名页 —— 应用程序动态分配的堆内存
- 无法直接释放
- 将这些内存通过 Swap 换出先存入磁盘中,然后释放给其他需要的进程
5.1.2 脏页写入磁盘的方式
- 在应用程序中,通过系统调用 fsync ,把脏页同步到磁盘
- 在系统中,通过内核线程 pdflush 负责这些脏页的刷新
5.1.3 swap 机制
Swap 将这些不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读取内存就可以了
5.2 Swap 原理
Swap 其实是将一块磁盘空间或一个本地文件,当成内存使用,存在以下两个过程:
- 换出
- 把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存
- 换入
- 在进程再次访问这些内存的时候,把他们从磁盘读取到内存中
5.2.1 直接内存回收
有新的大块内存分配请求,但是剩余内存不足。这个时候系统就需要回收一部分内存(比如前面提到的缓存),进而尽可能地满足新内存请求。
5.2.2 内核线程进行定期内存回收 —— kswapd0
kswapd0水位:
- 页最小阈值
- 页低阈值
- 页高阈值
- pages_free
kswapd0 定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。
- 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存。
- 剩余内存落在页最小阈值和页低阈值中间,说明内存压力比较大,剩余内存不多了。这时 kswapd0 会执行内存回收,直到剩余内存大于高阈值为止。
- 剩余内存落在页低阈值和页高阈值中间,说明内存有一定压力,但还可以满足新内存请求。
- 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力。
5.3 NUMA 与 Swap
情景:
明明发现了 Swap 升高,可是在分析系统的内存使用时,却很可能发现,系统剩余内存还多着呢。为什么剩余内存很多的情况下,也会发生 Swap 呢?
处理器的 NUMA 架构导致
在 NUMA 架构下,多个处理器被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间。
#查看处理器在Node 的分布情况
numactl --hardware
页最小阈值、页低阈值和页高阈值,可以通过 /proc/zoneinfo 来查看
cat /proc/zoneinfo
Node 0, zone Normal
pages free 363433
min 9773
low 12216
high 14659
scanned 0
spanned 5439488
present 5439488
managed 5340227
nr_free_pages 164964
指标说明:
- pages 处的 min、low、high,就是上面提到的三个内存阈值,而 free 是剩余内存页数,它跟后面的 nr_free_pages 相同
当某个 Node 内存不足时,系统可以从其他 Node 寻找空闲内存,也可以从本地内存中回收内存,具体模式可以通过 /proc/sys/vm/zone_reclaim_mode 来调整。
- 默认为 0 ,表示既可以从其他 Node 寻找空闲内存,也可以从本地回收内存
- 1、2、4 都表示只回收本地内存,2 表示可以回写脏数据回收内存,4 表示可以用 Swap 方式回收内存
5.4 swappiness
5.4.1 不同页的内存回收机制
- 文件页的回收
- 直接回收缓存,或者把脏页写回磁盘后再回收
- 匿名页的回收
- 通过swap机制,把他们写入磁盘后再释放内存
/proc/sys/vm/swappiness 用于调整使用 Swap 的积极程度,swappiness 的范围是0-100,数值越大,越积极使用 Swap,也就更倾向于回收匿名页;数值越小,越消极使用 Swap,也就是更倾向于回收文件页
5.5 Swap 使用升高时的定位与分析
5.5.1 案例 —— dd/sar
Linux 本身支持两种类型的 Swap,即 Swap 分区和 Swap 文件。
# 创建Swap文件
$ fallocate -l 8G /mnt/swapfile
# 修改权限只有根用户可以访问
$ chmod 600 /mnt/swapfile
# 配置Swap文件
$ mkswap /mnt/swapfile
# 开启Swap
$ swapon /mnt/swapfile
模拟大文件的读取:
# 写入空设备,实际上只有磁盘的读请求
$ dd if=/dev/sda1 of=/dev/null bs=1G count=2048
查看内存各个指标的变化情况:
# 间隔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
- 第一个表格表示内存的使用情况
- 第二个表格表示 Swap 的使用情况
- kb前缀表示这些指标的单位是 KB
指标介绍:
- kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需要内存的估计值。%commit,就是这个值相对总内存的百分比。
- kbactive,表示活跃内存,也就是最近使用过的内存,一般不会被系统回收。
- kbinact,表示非活跃内存,也就是不常访问的内存,有可能会被系统回收。
现象描述:
- 剩余内存(kbmemfree)不断减少,缓存区(kbbuffers)不断增大,可知,剩余内存不断分配给了缓存区
- 当剩余内存变小,缓冲区占用了大部分的内存,此时 Swap 的使用(%memused)开始逐渐增大,缓冲区和剩余内存只在小范围内波动
#查看缓存的使用情况
cachetop 5
#观察剩余内存、内存阈值以及匿名页和文件页的活跃情况
/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)的值。
剩余内存和缓冲区的波动变化,正是由于内存回收和缓存再次分配的循环往复。
- 当剩余内存小于页低阈值时,系统会回收一些缓存和匿名内存,使剩余内存增大。
- 缓存的回收导致 sar 中的缓冲区减小,而匿名内存的回收导致了 Swap 的使用增大。
- 由于dd进程还在继续,剩余内存又会重新分配给缓存,导致剩余内存减少,缓冲区增大
5.6 总结
- 内存资源紧张时,Linux 通过直接内存回收和定期扫描的方式,来释放文件页和匿名页,以便把内存分配给更需要的进程使用。
- 文件页回收
- 直接清空
- 把脏数据写回磁盘后再释放
- 匿名页回收
- Swap 换出磁盘,下次访问时,再从磁盘换入内存
- Swap 内存的换入换出
- 换出
- 将进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存
- 换入
- 在进程再次访问内存时,将他们从磁盘换入到内存中
- 调整系统定期回收内存的阈值 —— /proc/sys/vm/min_free_kbytes
- 调整文件页和匿名页的回收倾向 —— /proc/sys/vm/swappiness
- 当 Swap 变高时,可以用 sar、/proc/zoneinfo、/proc/pid/status 等方法,查看系统和进程的内存使用情况,找出 Swap 升高的根源和受影响的进程
- 降低 Swap 的使用,提高系统的整体性能。常见的降低方法:
- 禁止 Swap,现在服务器的内存足够大,所以除非有必要,禁用 Swap 就可以了。随着云计算的普及,大部分云平台中的虚拟机都默认禁止 Swap。
- 如果实在需要用到 Swap,可以尝试降低 swappiness 的值,减少内存回收时 Swap 的使用倾向。
- 响应延迟敏感的应用,如果它们可能在开启 Swap 的服务器中运行,你还可以用库函数 mlock() 或者 mlockall() 锁定内存,阻止它们的内存换出。
#查看使用swap最多的进程
//按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
#关闭swap
swapoff -a
#关闭swap再重新打开
swapoff -a && swapon -a
#命令可以直接将进程按照swap使用量排序显示
smem --sort swap
问题:
为什么大部分应用都关闭swap分区?
1、Elasticsearch/JAVA应用
1)Elasticsearch的内存使用情况由JVM控制
2)JVM 的 gc 有关,它在 gc 的时候会遍历所有用到的堆的内存,如果这部分内存是被 swap 出去了,遍历的时候就会有磁盘IO
2、kubernetes
1)性能问题,开启swap会严重影响性能(包括内存和I/O)
2)管理问题,开启swap后通过cgroups设置的内存上限就会失效
3、嵌入式系统
减少写的次数,延长Flash存储的寿命