Swap 把匿名页(堆、栈、malloc 分配)搬到磁盘/压缩后端,腾出物理 RAM。“swap 是不是该关掉”是运维永恒争论。
一、先看图
flowchart LR
ANON[匿名页 inactive] --> REC[回收器]
REC -->|磁盘 swap| SDEV[swap 分区/文件<br/>/dev/sda2]
REC -->|zram| ZRAM[zram 设备<br/>内存压缩]
REC -->|zswap| ZSWAP[zswap 压缩池<br/>→ 满时泄到 swap]
SDEV --> SWAPIN[swap in<br/>major fault]
ZRAM --> SWAPIN
ZSWAP --> SWAPIN
SWAPIN --> ANON2[恢复匿名页]
classDef mem fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef disk fill:#f0883e22,stroke:#f0883e,color:#adbac7;
classDef comp fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class ANON,ANON2,REC mem
class SDEV disk
class ZRAM,ZSWAP comp
二、基础
2.1 swap area
swapon --show
# NAME TYPE SIZE USED PRIO
# /dev/sda2 partition 8G 1.2G -2
# /swapfile file 4G 0B -3优先级高的先用。分区 swap 性能略好于文件 swap(少一层 VFS)。
2.2 swap entry
页被换出后,PTE 里存的不是物理地址,而是 swap entry:
bit 0 = 0 (不 present)
bit 1..62 = swap type + offset
缺页时内核解析 entry → 找到 swap 位置 → 读回。
2.3 swap cache
刚 swap-in 的页暂留 swap cache——如果马上又要换出,省一次写。page 同时有物理地址和 swap entry。
三、swappiness 的真实含义
D-36 提过,再强调:
- 不是 “swap 概率” 或 “swap 使用量上限”
- 是 回收扫描时 anon vs file 的权重
vm.swappiness = 60 → 扫 anon 和 file 的比例约 60:140
= 0 → 几乎只扫 file(除非没 file 可回收)
= 200 → anon 权重极高(配合 zram 有意义)
误区
- “swappiness=0 就不 swap” ❌(极端压力仍会)
- “swappiness=100 疯狂 swap” ❌(只是等权重)
四、zram
zram 创建一个内存中的块设备,数据压缩存储:
modprobe zram
echo lz4 > /sys/block/zram0/comp_algorithm
echo 4G > /sys/block/zram0/disksize
mkswap /dev/zram0
swapon -p 100 /dev/zram0压缩比通常 2-3x → 4GB zram ≈ 多出 8-12GB “虚拟 RAM”。
场景: - Android 标配(Chrome OS 也是) - 嵌入式、低内存 VPS - 桌面混合用(zram + 小 swapfile 做 fallback)
开销:压缩/解压 CPU;lz4 很快(几 μs/页),zstd 压得更紧但稍慢。
五、zswap
zswap 是 swap 的前端压缩池——页先压缩到内存池;池满才泄到真实 swap。
echo 1 > /sys/module/zswap/parameters/enabled
echo lz4 > /sys/module/zswap/parameters/compressor
echo 20 > /sys/module/zswap/parameters/max_pool_percent
echo zsmalloc > /sys/module/zswap/parameters/zpoolvs zram:
- zram 是独立块设备;zswap 是 swap 路径上的一层
- zswap 需要底层 swap area 做后端
- 内核 6.5+ zswap 支持 shrinker,可以淘汰冷压缩页
何时选哪个
- 只要压缩内存、不要磁盘 swap → zram
- 有磁盘 swap 但想减少 I/O → zswap
- 不能出 OOM → 两者 + swap 分区都开
六、swap readahead
swap in 时预读相邻的 swap 页面:
- cluster readahead:在 swap slot 连续的邻居里预读
- vma readahead:按 VMA 虚拟地址相邻预读
page_cluster sysctl 控制(默认 3 → 2^3 = 8
页)。SSD 上设小(0-1),HDD 上保持默认。
七、容器里的 swap
7.1 cgroup v2
memory.swap.max = 2G # 容器 swap 上限
memory.swap.current # 当前 swap 用量
memory.zswap.max = 1G # zswap 配额(6.5+)
7.2 Kubernetes
kubelet 长期要求关 swap。1.28+ beta 支持
swap(NodeSwap feature gate)。
LimitedSwap:只允许 Burstable podUnlimitedSwap:所有 pod 可 swap
生产建议:先在非关键节点测试。
7.3 DB 容器
数据库几乎都建议关 swap 或 swappiness=1。swap 带来的延迟不可预测——宁可 OOM 快速重启。
八、swap 还值得开吗?
开的理由: - 给内存压力一个缓冲——OOM Killer 是最后手段 - 不常用的后台进程占页能被换出 - zram 几乎零成本
关的理由: - 数据库、实时系统不能容忍 swap 延迟 - 大内存服务器(512GB+)swap 意义不大 - Kubernetes 历史上不支持
折中方案: - zram + swappiness=100(Android/ChromeOS 风格) - 小 swap + vm.swappiness=1(服务器风格)
九、诊断
free -h # Swap 行
vmstat 1 # si/so 列
sar -W 1 # pswpin/s pswpout/s
cat /proc/meminfo | grep -i swap
# 哪个进程在 swap
awk '/VmSwap/{if($2>0)print FILENAME,$0}' /proc/*/status 2>/dev/null | sort -k3 -rn | head
# swap I/O 延迟
bpftrace -e 'kprobe:swap_readpage { @start[tid]=nsecs; }
kretprobe:swap_readpage /@start[tid]/ { @us=hist((nsecs-@start[tid])/1000); delete(@start[tid]); }'十、小结
- swap 是 anon 页的”后备”——磁盘/zram/zswap 三条路
- swappiness 是权重不是概率
- zram 是内存压缩,Android/ChromeOS 标配
- zswap 是 swap 前端缓冲,减少磁盘 I/O
- 容器 swap 正在解冻,但生产需审慎
参考文献
Documentation/admin-guide/mm/zswap.rst、Documentation/admin-guide/blockdev/zram.rst- Corbet, J. “Zswap: compressed swap caching.” LWN.net 2013
- Seth Jennings, “zswap: compressed swap caching.” Kernel 3.11
mm/swap_state.c、mm/zswap.c、drivers/block/zram/- Kubernetes KEP-2400: Node Swap Support
工具
swapon --show、free -hvmstat、sar -Wzramctl/proc/meminfo、/sys/block/zram0/bpftraceswap tracing
延伸阅读
上一篇:内存回收 下一篇:OOM Killer
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。
【操作系统百科】io_uring 内核内部
io_uring 用共享内存 ring buffer 实现零 syscall 异步 I/O——SQ/CQ、SQPOLL、IOPOLL、注册 fd/buffer、multishot、安全模型演化。本文深入内核实现与工程实践。