linux性能优化系统Swap变高原因分析
Posted sysu_lluozh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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到底在什么时候需要回收内存呢?一直在说内存资源紧张,又该怎么来衡量内存是不是紧张呢?
- 直接内存回收
一个最容易想到的场景就是,有新的大块内存分配请求但是剩余内存不足,这个时候系统就需要回收一部分内存(比如缓存),进而尽可能地满足新内存请求。这个过程通常被称为直接内存回收
- 定期回收内存
除了直接内存回收,还有一个专门的内核线程用来定期回收内存,也就是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()锁定内存,阻止它们的内存换出
以上是关于linux性能优化系统Swap变高原因分析的主要内容,如果未能解决你的问题,请参考以下文章