要确认 swap 的是 anon page 还是 file-backed page,需结合 /proc/vmstat 中的 pswpin/pswpout 与 /proc/*/smaps 的 Swap 字段,并用 pmap -x 观察 ANON 和 SWAP 列是否同步增长:若 ANON 高且 SWAP 同步上升,则为 anon page 被 swap。

怎么确认 swap 的到底是 anon page 还是 file-backed page
Linux 的 swappiness 只影响内核在内存压力下「倾向」换出哪类页,但不禁止换出任何一类。所以即使设成 swappiness=10,swap 仍可能高频发生——关键得先分清:到底是谁在被 swap?
用 /proc/vmstat 最直接:pgpgin/pgpgout 是 I/O 页计数,不区分类型;真正要看的是 pgmajfault 和 /proc/*/smaps 中的 Swap 字段,但更高效的是:
- 运行
cat /proc/vmstat | grep -E "pgpg|pgmaj|pgpg",重点看pswpin和pswpout(单位:页),这两个是纯 swap I/O 计数 - 查具体进程:用
awk '/^Swap:/ {sum += $2} END {print sum}' /proc/*/smaps 2>/dev/null | sort -n | tail -5,结果是每个进程当前占用的 swap 页数(KB) - 结合
pmap -x看某进程的ANON列(匿名映射)和SWAP列是否同步增长——如果ANON高且SWAP也高,基本就是 anon page 在被 swap
为什么 swappiness=10 还会大量 swap anon page
swappiness 不是开关,而是权重调节器:它控制的是「在 reclaim 时,从 anon list 和 file list 中按比例选择候选页」。当系统真的缺内存,且 anon pages 占比极高(比如 Java 堆、大 malloc 缓存),哪怕权重低,reclaim 也必须扫到它们。
- 典型场景:Java 应用堆设了
-Xmx8g,但物理内存只剩 2G 可用,此时无论swappiness是 1 还是 100,内核都得把部分 anon page 换出 -
swappiness=0才是「尽量不 swap anon」,但它不等于「禁止」,仅表示:只有在 file cache 几乎清空后,才考虑 anon;而swappiness=10仍会给 anon 分配约 10% 的回收机会 - 注意:如果
/proc/sys/vm/swapiness显示为 10,但/proc/sys/vm/overcommit_memory是 2(严格模式),且/proc/sys/vm/overcommit_ratio设得太小,也会导致 malloc 失败前被迫提前 swap
file-backed page 被 swap 的常见诱因
file-backed page(如 mmap 的普通文件、共享库)本不该常驻 swap,因为它们有磁盘源,可直接丢弃或重读。但以下情况会让它们进 swap:
- 进程对 mmap 文件做了
MAP_PRIVATE+ 写操作 → 触发 COW,生成私有 dirty anon page,这部分就归入 anon list,后续可能被 swap - tmpfs/shm 使用过量(比如
/dev/shm被塞满),tmpfs 页面本质是 file-backed,但内存不足时会被当作 anon 处理并 swap - ext4/xfs 的 delayed allocation + 大量未刷盘 dirty page,在内存紧张时,内核可能先把它们标记为 swap candidate(尤其配合
vm.vfs_cache_pressure偏高时)
验证方法:用 find /dev/shm /run -type f -size +1M 2>/dev/null | xargs ls -lh 查大 tmpfs 文件;用 grep -i "mmapped" /proc/*/maps | wc -l 看有多少私有写时复制映射。
排查时最容易忽略的三个点
很多同学盯着 swappiness 调来调去,却漏掉底层约束:
-
/proc/sys/vm/min_free_kbytes设得太低,会导致 kswapd 过早唤醒,频繁扫描页表,放大 swap 行为——建议设为物理内存的 1%~2% -
/proc/sys/vm/dirty_ratio和/proc/sys/vm/dirty_background_ratio若设得过高(比如 40/10),会使脏页堆积,间接抬高内存水位,触发更激进的 reclaim - NUMA 架构下,
numactl --membind=0 your_app启动的进程若只绑定了 node 0,但 node 0 内存耗尽,node 1 又没被允许访问,就会强行 swap,而不是跨 node 分配
swap 频繁从来不是单一参数问题,而是内存分配路径、页面生命周期、I/O 压力三者咬合的结果。改 swappiness 前,先看 pswpout 是否真在涨,再确认涨的是谁的 Swap: 字段——否则调再低也没用。










