请求分页与抖动 — 内存不够时会发生什么
一句话总结
只在需要时载入页面,可以运行比物理内存更大的程序;但一次缺页的成本比内存访问高数万倍,缺页率稍有上升就可能拖垮系统。
为什么需要它
程序不必整体装入内存。错误处理代码等部分很少执行,大数组也常只使用一小部分。按需分页只在使用时载入页面,既能同时运行更多进程,也能缩短程序启动时间。
工作原理
页表项含有效位。访问无效页会触发缺页异常,处理顺序如下:
- 触发陷阱,进入内核。
- 判断是非法地址还是尚未载入的页面。
- 寻找空闲页框;若没有,则选择牺牲页换出。若该页已修改(dirty),先写入磁盘。
- 从磁盘读取页面并装入页框。
- 更新页表,从导致异常的那条指令重新执行。
成本十分惊人。假设内存访问为 200ns,缺页处理为 8ms,缺页概率为 p,则有效访问时间是 (1−p)×200 + p×8,000,000。
- p = 0.001,即每千次一次缺页时,有效访问约 8,200ns,比无缺页慢 40 倍。
- 要把性能下降控制在 10% 内,p 必须不高于约 0.0000025,即40 万次才一次。
页面置换算法也各有特点。对引用串 7 0 1 2 0 3 0 4 2 3 0 3 2 1 2 0 1 7 0 1 分配 3 个页框时,FIFO 缺页 15 次,LRU 12 次,理论最优 OPT 9 次。FIFO 甚至存在增加页框后缺页反而增多的Belady 异常。实际系统不用纯 LRU,而常用基于引用位的**时钟(second-chance)**算法,以更低成本取得接近 LRU 的效果。
抖动(thrashing) 是页框不足导致缺页激增、CPU 因此空闲,而操作系统误以为负载不高又加入更多进程,使情况继续恶化的循环。应跟踪工作集(近期实际引用的页面集合)并保证足够页框,或直接监控缺页率调整分配。
实际工作中的表现
容器设定内存上限后可能有两种结局。关闭交换时,OOM Killer 会杀死进程,虽突然但原因明确。启用交换时则可能开始抖动:进程还活着,响应却慢几十倍,CPU 使用率低而 iowait 飙升。因此常说被杀反而更容易诊断。Kubernetes 长期要求关闭交换,也源于这种不可预测性。
后续测验将确认什么
确认你能亲自计算缺页成本,并解释抖动为何是会自我强化的恶性循环。