文章目录
- 1.一、什么是文件页?什么是脏页?什么是匿名页?
- 二、linux swap原理
- 三、内存回收的时机
- 1、直接内存回收
- 2、kswapd0内核线程
- 四、内存回收的方式
- 四、NUMA 与 Swap关系
- 五、swappiness
1.一、什么是文件页?什么是脏页?什么是匿名页?
- 1、缓存和缓冲区,就属于可回收内存。它们在内存管理中,通常被叫做文件页(File-backed Page),
此外除了缓存和缓冲区,通过内存映射获取的文件映射页,也是一种常见的文件页。它也可以被释放掉,下次再访问的时候,从文件重新读取。 - 2、大部分文件页,都可以直接回收,以后有需要时,再从磁盘重新读取就可以了。而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能进行内存释放。
脏页的写入磁盘的方式:
(1)系统调用 fsync ,把脏页同步到磁盘中
(2)也可以交给系统,由内核线程 pdflush 负责这些脏页的刷新 - 3、应用程序动态分配的堆内存称为匿名页(Anonymous Page)
堆内存很可能还要再次被访问,当然不能直接回收了。这些内存自然不能直接释放。
二、linux swap原理
- Swap 把这些不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以了。
- 换出
把进程暂时不用的内存数据(经过上文分析主要是堆内存)存储到磁盘中,并释放这些数据占用的内存 - 换入
把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存 - 给人的感觉是Swap 其实是把系统的可用内存变大了。这样,即使服务器的内存不足,也可以运行大内存的应用程序。其实在现在内存比较廉价的年代,对于追求高性能的业务完全可以关闭swap,因为内存和硬盘的速度在目前还存在瓶颈。
- 典型场景:
(1)即使内存不足时,有些应用程序也并不想被 OOM 杀死,而是希望能缓一段时间,等待人工介入,或者等系统自动释放其他进程的内存,再分配给它。
(2)我们常见的笔记本电脑的休眠和快速开机的功能,也基于 Swap 。休眠时,把系统的内存存入磁盘,这样等到再次开机时,只要从磁盘中加载内存就可以。这样就省去了很多应用程序的初始化过程,加快了开机速度
三、内存回收的时机
1、直接内存回收
有新的大块内存分配请求,但是剩余内存不足。这个时候系统就需要回收一部分内存(比如前面提到的缓存),进而尽可能地满足新内存请求。这个过程通常被称为直接内存回收
2、kswapd0内核线程
除了直接内存回收,还有一个专门的内核线程用来定期回收内存,也就是kswapd0。
为了衡量内存的使用情况,kswapd0 定义了三个内存阈值(watermark,也称为水位)
- 页最小阈值(pages_min)
- 页低阈值(pages_low)
- 页高阈值(pages_high)
剩余内存,则使用 pages_free 表。
kswapd0 定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作 - 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存。
- 剩余内存落在页最小阈值和页低阈值中间,说明内存压力比较大,剩余内存不多了。这时 kswapd0 会执行内存回收,直到剩余- 内存大于高阈值为止。
- 剩余内存落在页低阈值和页高阈值中间,说明内存有一定压力,但还可以满足新内存请求
- 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力。
一旦剩余内存小于页低阈值,就会触发内存的回收。
这个页低阈值,其实可以通过内核选项 /proc/sys/vm/min_free_kbytes 来间接设置。min_free_kbytes 设置了页最小阈值,而其他两个阈值,都是根据页最小阈值计算生成的,计算方法如下
四、内存回收的方式
一旦发现内存紧张,系统会通过三种方式回收内存。这三种方式分别是 :
- 基于 LRU(Least Recently Used)算法,回收缓存;
- 基于 Swap 机制,回收不常访问的匿名页;
- 基于 OOM(Out of Memory)机制,杀掉占用大量内存的进程
前两种方式,缓存回收和 Swap 回收,实际上都是基于 LRU 算法,也就是优先回收不常访问的内存。LRU 回收算法,实际上维护着 active 和 inactive 两个双向链表,其中:
- active 记录活跃的内存页;
- inactive 记录非活跃的内存页
越接近链表尾部,就表示内存页越不常访问。这样,在回收内存时,
系统就可以根据活跃程度,优先回收不活跃的内存。
活跃和非活跃的内存页,按照类型的不同,又分别分为文件页和匿名页,对应着缓存回收和 Swap 回收。
第三种方式,OOM 机制按照 oom_score 给进程排序。oom_score 越大,进程就越容易被系统杀死
- 当系统发现内存不足以分配新的内存请求时,就会尝试直接内存回收。这种情况下,如果回收完文件页和匿名页后,内存够用了,当然皆大欢喜,把回收回来的内存分配给进程就可以了。但如果内存还是不足,OOM 就要登场。
- OOM 发生时,你可以在 dmesg 中看到 Out of memory 的信息,从而知道是哪些进程被 OOM 杀死了。比如,你可以执行下面的命令,查询 OOM 日志
OOM什么时候会发生?
- OOM 触发的时机基于虚拟内存。换句话说,进程在申请内存时,如果申请的虚拟内存加上服务器实际已用的内存之和,比总的物理内存还大,就会触发 OOM
四、NUMA 与 Swap关系
为什么剩余内存很多的情况下,也会发生 Swap 呢?这正是处理器的 NUMA (Non-UniformMemory Access)架构导致的。
在 NUMA 架构下,多个处理器被划分到不同 Node 上,且每个 Node 都拥有自己的本地内存空间。而同一个 Node 内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),比如直接内存访问区(DMA)、普通内存区(NORMAL)、伪内存区(MOVABLE)等,
如下图所示
- 每个 Node 都有自己的本地内存空间
- 我们只要会查看这些内存域的阈值的配置,以及缓存、匿名页的实际使用情况就够了
numactl 命令,来查看处理器在 Node 的分布情况
NUNA 和Swap之间的关系:
- 前面提到的三个内存阈值(页最小阈值、页低阈值和页高阈值),都可以通过内存域在 proc 文件系统中的接口 /proc/zoneinfo 来查看
- ages 处的 min、low、high,就是上面提到的三个内存阈值,而 free 是剩余内存页数,它跟后面的 nr_free_pages 相同。
- nr_zone_active_anon 和 nr_zone_inactive_anon,分别是活跃和非活跃的匿名页数。
- nr_zone_active_file 和 nr_zone_inactive_file,分别是活跃和非活跃的文件页数
- 剩余内存远大于页高阈值,所以此时的 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。