系统优化-内存-Swap机制

内存回收

之前提到当linux的内存不足时候会通过oom机制来kill程序加速释放内存,除了这种方法,linux还可以在资源不足时候释放可以回收的内存。哪些是可回收的内存呢?

  • buffer/cache:这些被成为文件页(File-backed Page)大部分文件页可以直接回收,需要了在从磁盘读取,如果系统只修改了内存还没有写入到磁盘,我们成为脏页,需要写回磁盘在就进行回收
    • 可以在应用程序中,通过系统调用 fsync ,把脏页同步到磁盘中;
    • 也可以交给系统,由内核线程 pdflush 负责这些脏页的刷新;
  • 应用程序动态分配的内存,我们称之为匿名页(Anonymous Page),会通过Swap换入/换出的方式将内存写入到磁盘中来释放内存。

Swap的机制介绍

swap将补偿访问的内存通过换入将内存数据写到磁盘中,需要了再从磁盘读取这实际上是一种用时间换空间的思想。

回收方式

  1. 直接回收:大部分动态分配的内存都可以直接回收
  2. kswapd0:一个专门的内核线程用来定期回收内存。为了衡量内存的使用情况,kswapd0定义了三个内存阈值(watermark,也称为水位),分别是页最小阈值(page min),页最小阈值(pages_min)、页低阈值(pages_low)和页高阈值(pages_high)。剩余内存,则使用 pages_free 表示。这里,我画了一张图表示它们的关系。

    avator

    kswapd0的运行机制如下:

    小于pages_min:内存页耗尽
    小于pages_low:内存页不足,kswapd0 会执行内存回收,直到剩余内存大于高阈值为止
    小于pages_high:内存资源还可以满足申请的需求
    大于pages_high:内存资源充足

    我们可以通过修改/proc/sys/vm/min_free_kbytes来修改pages_min,其他的pages_low和pages_high都是通过pages_min计算出来的。

NUMA 与 Swap

NUMBA是(Non-Uniform Memory Access),多个cpu会被划分到不通的node中,我们可以用numbactl看到如下的情况,只有一个node,对应了0,1俩个cpu,有7977MB的内存,空余内存是4416MB。

1
2
3
4
5
6
$ numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1
node 0 size: 7977 MB
node 0 free: 4416 MB
...

实际上,前面提到的三个内存阈值(页最小阈值、页低阈值和页高阈值),都可以通过内存域在 proc 文件系统中的接口 /proc/zoneinfo 来查看。比如,下面就是一个 /proc/zoneinfo 文件的内容示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

$ cat /proc/zoneinfo
...
Node 0, zone Normal
pages free 227894
min 14896 ## 三个水位线
low 18620
high 22344
...
##空闲page和上面的free对应
nr_free_pages 227894
##活跃和非活跃的匿名页数
nr_zone_inactive_anon 11082
nr_zone_active_anon 14024
##活跃和非活跃的文件页数
nr_zone_inactive_file 539024
nr_zone_active_file 923986
...

内存资源紧张时候,默认采用回收匿名页数以及文件页数的方式来回收内存,可以通过/proc/sys/vm/zone_reclaim_mode调整

  • 默认的 0 ,也就是刚刚提到的模式,表示既可以从其他 Node 寻找空闲内存,也可以从本地回收内存。
  • 1、2、4 都表示只回收本地内存,2 表示可以回写脏数据回收内存,4 表示可以用 Swap 方式回收内存。

swappiness

内存回收有俩种方式

  1. 回收文件页:通过fsync将buffer和cache写入到磁盘来释放这一部分内存
  2. 回收匿名页:通过swap来释放匿名页内存

    Linux 提供了一个 /proc/sys/vm/swappiness 选项,用来调整使用 Swap 的积极程度。范围是 0-100,数值越大,越积极使用 Swap,也就是更倾向于回收匿名页;数值越小,越消极使用 Swap,也就是更倾向于回收文件页。

    注意swappiness只是一个趋势。

案例排查

首先确认swap开启,通过free确认,如果没开启可用下面方法开启

1
2
3
4
5
6
7
8
9

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

测试方案:用命令dd

1
2
# 写入空设备,实际上只有磁盘的读请求
$ dd if=/dev/sda1 of=/dev/null bs=1G count=2048

这时候我们用sar -r命令观察,我们发现kbmemfree值越来越少,%memused越来越大,且kbbuffers越来越大,因为dd直接和磁盘交互,符合预期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 间隔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
  • 刚开始,剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,由此可知,剩余内存不断分配给了缓冲区。
  • 一段时间后,剩余内存已经很小,而缓冲区占用了大部分内存。这时候,Swap 的使用开始逐渐增大,缓冲区和剩余内存则只在小范围内波动。

为什么缓冲区只在小范围内波动?由哪些进程导致,这时候我们用cachetop进行进一步排查发现是dd导致的,他的READ_HIT只有50%命中率且Miss的page已经到了41022页,可见也符合我们猜测的预期。

1
2
3
4
5
$ 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%

为什么Swap开始升高了?不是应该释放buffer么?我们查看/proc/zoneinfo返现,内存小于low,内存就开始执行回收才做恢复到high附近,周而复始。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# -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

我们发现有时候释放的是未知页即直接内存有时候是文件页即buffer,这是由于我们swappiness配置导致的

1
2
cat /proc/sys/vm/swappiness
60