1. 核心整体关系
页面置换算法 和 Swap 是配套机制:
页面置换算法:负责内存紧张时,挑选哪一页淘汰
Swap:负责把淘汰下来、无磁盘原生副本的页面,落地到磁盘保存
缺页异常 + 无空闲物理页 → 置换算法选冷页淘汰 → 根据页面类型决定是否写 Swap。
2. 四大经典页面置换算法(完整理论)
OPT 最优置换算法
理论最优,淘汰未来最长时间不访问的页面。 缺页率最低,但需要预知未来访问序列,无法工程实现,仅作为理论评测标准。
FIFO 先进先出
按照页面载入内存顺序淘汰最旧页面。 缺点:不区分冷热,长期高频使用的老页也会被淘汰;存在 Belady 异常(内存页框越多,缺页次数反而增加)。
LRU 最近最少使用
基于局部性原理:过去很久没用 → 未来大概率不用。 淘汰最近最少访问的页面,实际效果最贴合业务行为。 缺点:严格 LRU 需要维护完整时间有序链表,开销巨大,操作系统不采用。
Clock 时钟算法(Linux 工程实现)
LRU 的轻量化近似实现。 利用硬件 A 访问位 环形扫描页面:
A=1:近期访问过,清零,保留
A=0:长期未访问,直接淘汰 开销极低,是 Linux 内核真实回收的核心思路。
3. Linux 真实内存回收机制
维护两条链表
active 活跃链表:热页、高频访问
inactive 非活跃链表:冷页、优先回收
硬件自动维护位
A 访问位:判断页面冷热
D 脏位:判断页面是否需要写盘
kswapd 后台线程 内存低于水位线提前后台预回收,不等内存彻底撑满,避免系统卡顿。
4. 系统两类页面 + 回收规则
1)文件页(依托 ELF 原始磁盘副本)
.text / .rodata
PTE 永久只读,用户态永远无法写入。 硬件无法触发写操作 → D 脏位永远为 0,永远是干净页 回收规则:直接释放物理内存,无需写盘、无需 Swap
.data(可读写全局变量)
初始为私有文件映射:
未写:D=0 干净页,回收直接释放
发生写操作:触发私有映射的 COW,内核复制出新物理页;新页面脱离文件映射,转为匿名页
👉 重点: 原始 ELF 文件页永远干净、永远不改;修改产生的新页是匿名页,后续回收走 Swap,绝不覆盖原始 ELF 程序文件
2)匿名页(Swap 唯一服务对象)
包含:堆、用户栈、.bss、COW 复制出来的 data 新页 特性:无原生磁盘文件副本
回收铁律:
匿名页无论 D=0 干净 / D=1 脏,只要被 LRU/Clock 选中回收,必须写入 Swap
原因: 匿名页没有磁盘备份,干净只是代表「本次载入后没修改」,不代表磁盘有数据,不写 Swap 数据直接丢失。
5. 页面换出 PageOut 完整流程
内存紧张,kswapd 触发页面回收
通过 Clock 近似 LRU 筛选最冷页面
分类处理:
text/rodata/data 干净文件页:直接释放
被写过的 data:已经转为匿名页 → 写入 Swap
堆 / 栈 /bss 匿名页:一律写入 Swap
PTE 置 Present=0,记录 Swap 磁盘偏移
释放物理内存
6. 页面换入 PageIn 完整流程
进程访问已换出的虚拟地址
MMU 检测 Present=0,触发缺页异常
内核判断页面存在 Swap 分区
分配空闲物理页,从磁盘读回页面数据
修复 PTE:回填物理页框号、Present=1
移入活跃链表,程序继续执行
7. 终极总结(面试直接背诵)
置换算法负责选淘汰页,Swap 负责持久化无副本匿名页。
text、rodata 只读无脏页,回收直接丢弃;data 写后通过 COW 转为匿名页。
文件页依靠 ELF 原生副本,无需 Swap;Swap 只服务匿名页。
Linux 放弃严格 LRU,使用 Clock 近似 LRU,兼顾性能与效果。
匿名页无论干净脏,回收必落 Swap,因为天生无磁盘备份。