Linux 内存测漏会导致内存紧张,从而触发系统直接回收内存和OOM。
- OOM 直接杀死进程从而释放内存
- 内存回收, 大部分文件页都可以直接回收,比如缓存和缓冲区,就属于可回收内存,他们在内存管理中,被叫文件页。
大部分文件页都可以直接被回收,以后有需要在从磁盘读取,而那些暂时还没写入磁盘的数据(脏页),就得先写入磁盘,然后在进行释放。
脏页的写入磁盘方式:
- 通过系统调用 fsync ,把脏页同步到磁盘中;
- 也可以交给系统,由内核线程 pdflush 负责这些脏页的刷新。 除了缓存和缓冲区,通过内存映射获取的文件映射页,也是一种常见的文件页。它也可以被释放掉,下次再访问的时候,从文件重新读取。
应用程序动态分配的堆内存,也就是匿名页(Anonymous Page)不能被直接回收。但是如果很少被访问,Linux 会将它们暂时存放在磁盘里面,也就是swap中,然后释放内存。
SWAP 原理
Swap 就是把一块磁盘空间或者一个本地文件(以下讲解以磁盘为例),当成内存来使用。它包括换出和换入两个过程。
除此之外,还定义了一个进程 kswapd0 来定期回收内存,kswapd0 定义了三个阈值,分别是
- 页最小阈值(pages_min)
- 页低阈值(pages_low)
- 页高阈值(pages_high)
剩余内存,则使用 pages_free 表示。
kswapd0定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。
- 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存。
- 剩余内存落在页最小阈值和页低阈值中间,说明内存压力比较大,剩余内存不多了。这时kswapd0会执行内存回收,直到剩余内存大于高阈值为止。
- 剩余内存落在页低阈值和页高阈值中间,说明内存有-定压力,但还可以满足新内存请求。●剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力。
一旦剩余内存小于页低阈值,就会触发内存的回收。这个页低阈值,其实可以通过内核选项 /proc/sys/vm/min_free_kbytes 来间接设置。min_free_kbytes 设置了页最小阈值,而其他两个阈值,都是根据页最小阈值计算生成的,计算方法如下 :
1 2
| pages_low = pages_min*5/4 pages_high = pages_min*3/2
|
##NUMA 和 swap
很多情况下,你明明发现了 Swap 升高,可是在分析系统的内存使用时,却很可能发现,系统剩余内存还多着呢。这有可能是 NUMA 架构导致的。
在 NUMA 架构下,多个处理器被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间。
而同一个 Node 内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),比如直接内存访问区(DMA)、普通内存区(NORMAL)、伪内存区(MOVABLE)等,如下图所示:
使用 numactl 查看 node 分布
1 2 3 4 5 6 7 8
| root@linux:~# numactl --hardware available: 1 nodes (0) node 0 cpus: 0 1 node 0 size: 7965 MB node 0 free: 7631 MB node distances: node 0 0: 10
|
上面我们发现系统就一个node 也就是node 0, 编号为 0和1,node 0 内存为 7965MB 剩余 7631. 查看阈值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $ 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_ fhle 和nr_ zone_ inactive_ fle, 分别是活跃和非活跃的文件页数。
从这个输出结果可以发现,剩余内存远大于页高阈值,所以此时的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。
案例
- 创建交换分区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| root@linux:~# free total used free shared buff/cache available Mem: 8156288 230724 6775564 7468 1150000 7665632 Swap: 0 0 0 root@linux:~# fallocate -l 8G /mnt/swapfile root@linux:~# chmod 600 /mnt/swapfile root@linux:~# mkswap /mnt/swapfile Setting up swapspace version 1, size = 8 GiB (8589930496 bytes) no label, UUID=ceee5c74-7eee-4c46-a354-9468a92d7722 root@linux:~# swapon /mnt/swapfile root@linux:~# free total used free shared buff/cache available Mem: 8156288 157116 7762788 6028 236384 7756172 Swap: 8388604 0 8388604 root@linux:~#
|
执行
1
| root@linux:~# dd if=/dev/vdc of=/dev/null bs=1G count=2048
|
执行 sar -r -S 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| # sar -r -S 1 # -r 表示显示内存使用情况,-S 表示显示swap 使用情况 04:48:55 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:48:56 PM 3736788 6692972 4419500 54.19 2967752 196772 1451516 8.77 1188364 3102932 212
04:48:55 PM kbswpfree kbswpused %swpused kbswpcad %swpcad 04:48:56 PM 8388604 0 0.00 0 0.00
04:48:56 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:48:57 PM 3626516 6696040 4529772 55.54 3080392 197252 1434824 8.67 1186304 3215884 384
04:48:56 PM kbswpfree kbswpused %swpused kbswpcad %swpcad 04:48:57 PM 8388604 0 0.00 0 0.00
04:48:57 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:48:58 PM 3514288 6695816 4642000 56.91 3192264 197252 1434824 8.67 1186308 3327760 452
04:48:57 PM kbswpfree kbswpused %swpused kbswpcad %swpcad 04:48:58 PM 8388604 0 0.00 0 0.00
04:48:58 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:48:59 PM 3401440 6695644 4754848 58.30 3304904 197208 1434824 8.67 1186308 3440356 452
04:48:58 PM kbswpfree kbswpused %swpused kbswpcad %swpcad 04:48:59 PM 8388604 0 0.00 0 0.00
04:48:59 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:49:00 PM 3288592 6695628 4867696 59.68 3417552 197252 1434824 8.67 1186364 3553004 56
04:48:59 PM kbswpfree kbswpused %swpused kbswpcad %swpcad 04:49:00 PM 8388604 0 0.00 0 0.00
04:49:00 PM kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 04:49:01 PM 3175620 6695400 4980668 61.07 3530192 197284 1434824 8.67 1186364 3665668 56
|
参数说明:
- kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需要内存的估计值。%commit,就是这个值相对总内存的百分比。
- kbactive, 表示活跃内存,也就是最近使用过的内存,一 般不会被系统回收。
- kbinact, 表示非活跃内存,也就是不常访问的内存,有可能会被系统回收。
发现总的内存使用率(%memused)在不断增长,从开始的 54% 一直长到了 61%,并且主要内存都被缓冲区(kbbuffers)占用。
- 刚开始,剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,由此可知,剩余内存不断分配给了缓冲区。
- 一段时间后,剩余内存已经很小,而缓冲区占用了大部分内存。这时候,Swap 的使用开始逐渐增大,缓冲区和剩余内存则只在小范围内波动。
使用cachetop 观察,发现 dd 读写请求只有50% 命中
1 2 3 4 5 6 7
| 16:52:14 Buffers MB: 5890 / Cached MB: 181 / Sort: HITS / Order: ascending PID UID CMD HITS MISSES DIRTIES READ_HIT% WRITE_HIT% 1003 syslog rs:main Q:Reg 4 1 1 60.0% 0.0% 1728 root cachetop 6 0 0 100.0% 0.0% 210 root jbd2/vda1-8 7 5 5 16.7% 16.7% 272 root systemd-journal 38 29 29 13.4% 0.0% 1670 root dd 141063 140816 0 50.0% 50.0%
|
执行 watch -d grep -A 15 ‘Normal’ /proc/zoneinfo 观察
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Every 2.0s: grep -A 15 Normal /proc/zoneinfo linux: Thu Mar 28 16:54:34 2019
Node 0, zone Normal pages free 17538 min 10560 low 13200 high 15840 spanned 1310720 present 1310720 managed 1269191 protection: (0, 0, 0, 0, 0) nr_free_pages 17538 nr_zone_inactive_anon 20379 nr_zone_active_anon 262562 nr_zone_inactive_file 918902 nr_zone_active_file 18037 nr_zone_unevictable 0 nr_zone_write_pending 44
|