前言
在内存资源紧张时,Linux通过直接内存回收和定期扫描的方式,来释放文件页和匿名页,以便把内存分配给更需要的进程使用
-
文件页的回收比较容易理解,直接清空缓存,或者把脏数据写回磁盘后,再释放缓存就可以了
-
匿名页则需要通过Swap换出到磁盘中,这样在下次访问的时候,再次从磁盘换入到内存中就可以了
开启Swap后,可以设置/proc/sys/vm/min_free_kbytes ,来调整系统定期回收内存的阈值
也可以设置 /proc/sys/vm/swappiness ,来调整文件页和匿名页的回收倾向
案例
-
环境准备
# 机器配置:2cpu,4GB内存 # 预先安装sysstat包 [root@local_sa_192-168-1-6 ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [root@local_sa_192-168-1-6 ~]# uname -r 5.15.7-1.el7.elrepo.x86_64 [root@local_sa_192-168-1-6 ~]# cat /proc/cpuinfo |grep 'model name'|wc -l 2 [root@local_sa_192-168-1-6 ~]# free total used free shared buff/cache available Mem: 4027980 322712 3495244 30236 210024 3467040 Swap: 8388604 0 8388604 #### PS # 如果没有swap,可以根据以下步骤创建 # Linux本身支持两种类型的Swap,即Swap分区和Swap文件 # 以swap文件为例,配置swap文件大小为8GB # 1.创建Swap文件 [root@local_sa_192-168-1-6 ~]# fallocate -l 8G /mnt/swapfile # 2.修改权限只有root用户可以访问 [root@local_sa_192-168-1-6 ~]# chmod 600 /mnt/swapfile # 3. 配置 Swap 文件 [root@local_sa_192-168-1-6 ~]# mkswap /mnt/swapfile 正在设置交换空间版本 1,大小 = 8388604 KiB 无标签,UUID=2187a892-77e1-4dd1-87cd-07c2bfb30c7e # 4.开启 Swap(如果需要开机启动,则把上面的UUID写入到/etc/fstab表中) [root@local_sa_192-168-1-6 ~]# swapon /mnt/swapfile
-
第一个终端中,运行dd命令,模拟大文件的读取
# 写入空设备,实际上只有磁盘的读请求 [root@local_sa_192-168-1-6 ~]# dd if=/dev/mapper/centos-root of=/dev/null bs=1G count=2048
-
在第二个终端中运行sar命令,查看内存各个指标的变化情况
# 间隔1秒输出一组数据 # -r表示显示内存使用情况 # -S表示显示Swap使用情况 [root@local_sa_192-168-1-6 ~]# sar -r -S 1 16时00分22秒 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 16时00分23秒 924660 3103320 77.04 819200 138048 4027236 32.43 92360 2859144 20 16时00分22秒 kbswpfree kbswpused %swpused kbswpcad %swpcad 16时00分23秒 8388604 0 0.00 0 0.00 16时00分25秒 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 16时00分26秒 115852 3912128 97.12 1403372 134376 4027448 32.44 92772 3675516 20 16时00分25秒 kbswpfree kbswpused %swpused kbswpcad %swpcad 16时00分26秒 8388604 0 0.00 0 0.00 ..... 16时00分38秒 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 16时00分39秒 99832 3928148 97.52 1421212 133728 4026076 32.42 94464 3690748 0 16时00分38秒 kbswpfree kbswpused %swpused kbswpcad %swpcad 16时00分39秒 8388336 268 0.00 0 0.00 16时00分39秒 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 16时00分40秒 121456 3906524 96.98 1388452 142876 4029224 32.45 93956 3668408 20 16时00分39秒 kbswpfree kbswpused %swpused kbswpcad %swpcad 16时00分40秒 8388080 524 0.01 148 28.24 16时00分43秒 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 16时00分44秒 112616 3915364 97.20 1392340 145424 4028368 32.44 92900 3675456 124 16时00分43秒 kbswpfree kbswpused %swpused kbswpcad %swpcad 16时00分44秒 8387824 780 0.01 108 13.85 16时00分47秒 kbmemfree kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 16时00分48秒 111288 3916692 97.24 1385396 146108 4032384 32.48 92776 3671704 40 16时00分47秒 kbswpfree kbswpused %swpused kbswpcad %swpcad 16时00分48秒 8387568 1036 0.01 200 19.31 ## sar的输出结果是两个表格,第一个表格表示内存的使用情况,第二个表格表示Swap的使用情况 各个指标名称前面的kb前缀,表示这些指标的单位是KB ## kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需要内存的估计值 %commit,就是这个值相对总内存的百分比 ## kbactive,表示活跃内存,也就是最近使用过的内存,一般不会被系统回收 kbinact,表示非活跃内存,也就是不常访问的内存,有可能会被系统回收 ## 从输出结果可以看到,总的内存使用率(%memused)在不断增长,从开始的77%一直长到了97% 并且主要内存都被缓冲区(kbbuffers)占用 剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大 由此可知,剩余内存不断分配给了缓冲区 一段时间后,剩余内存已经很小,而缓冲区占用了大部分内存 这时候,Swap的使用开始逐渐增大,缓冲区和剩余内存则只在小范围内波动 ## 为什么缓冲区在不停增大?这又是哪些进程导致的呢?
-
cachetop看看进程缓存的情况
[root@local_sa_192-168-1-6 ~]# cachetop 5 17:15:02 Buffers MB: 2254 / Cached MB: 201 / Sort: HITS / Order: descending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 1664 root dd 370541 397312 0 48.3% 51.7% 1667 root cachetop 2 0 0 100.0% 0.0% ## 通过cachetop的输出,dd进程的读写请求只有50%的命中率,并且未命中的缓存页数(MISSES)为 397312(单位是页) 这说明,正是案例开始时运行的dd,导致了缓冲区使用升高 ## 为什么Swap也跟着升高了呢?缓冲区占了系统绝大部分内存,还属于可回收内存,内存不够用时,不应该先回收缓冲区吗?
-
观察/proc/zoneinfo文件
# -d 表示高亮变化的字段 # -A 表示仅显示 Normal 行以及之后的 15 行输出 [root@local_sa_192-168-1-6 ~]# watch -d grep -A 15 'Normal' /proc/zoneinfo Every 2.0s: grep -A 15 Normal /proc/zoneinfo Mon Dec 13 17:31:03 2021 Node 0, zone Normal pages free 2311 min 1905 low 2381 high 2857 spanned 131072 present 131072 managed 112791 cma 0 protection: (0, 0, 0, 0, 0) nr_free_pages 2311 nr_zone_inactive_anon 38892 nr_zone_active_anon 186 nr_zone_inactive_file 20503 nr_zone_active_file 21185 nr_zone_unevictable 384 ## 可以发现,剩余内存(pages_free)在一个小范围内不停地波动。 当它小于页低阈值(pages_low)时,又会突然增大到一个大于页高阈值(pages_high)的值 ## 再结合刚刚用sar看到的剩余内存和缓冲区的变化情况 可以推导出剩余内存和缓冲区的波动变化,正是由于内存回收和缓存再次分配的循环往复 1. 当剩余内存小于页低阈值时,系统会回收一些缓存和匿名内存,使剩余内存增大 其中,缓存的回收导致sar中的缓冲区减小,而匿名内存的回收导致了Swap的使用增大 2.紧接着,由于dd还在继续,剩余内存又会重新分配给缓存,导致剩余内存减少,缓冲区增大
-
查看 swappiness 的配置
[root@local_sa_192-168-1-6 ~]# cat /proc/sys/vm/swappiness 60 ## swappiness显示的是默认值60,这是一个相对中和的配置 所以系统会根据实际运行情况,选择合适的回收类型,比如回收不活跃的匿名页,或者不活跃的文件页
-
查看使用 Swap 最多的进程
# 安装smem包 [root@local_sa_192-168-1-6 ~]# yum install smem -y # [root@local_sa_192-168-1-6 ~]# smem --sort swap PID User Command Swap USS PSS RSS 1 root /usr/lib/systemd/systemd -- 0 2760 2859 4956 539 root /usr/lib/systemd/systemd-jo 0 1240 1729 4096 566 root /usr/sbin/lvmetad -f 0 2752 2909 5312 575 root /usr/lib/systemd/systemd-ud 0 1788 1868 3960 1134 www php-fpm: pool www 1116 208 246 9580 1135 www php-fpm: pool www 1116 208 246 9580 1136 www php-fpm: pool www 1116 208 246 9580 1507 root /usr/bin/sudo -E LD_LIBRARY 1164 12 494 6192 1599 root /usr/bin/sudo -E LD_LIBRARY 1168 40 512 6212 1632 root /usr/bin/sudo -E LD_LIBRARY 1168 8 501 6200
小结
在内存资源紧张时,Linux 会通过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()锁定内存,阻止它们的内存换出