资讯动态

swap占用高但物理内存还有富余?Linux内存回收机制排查与处置全解

发布时间:2026/10/22 0:40:37 来源:尧图企业网站定制
凌晨两点值班群弹出一条系统告警某台云主机 swap 使用率 87%持续超过 15 分钟。登录上去一看free -h显示物理内存还剩 20 多 GB。很多人的第一反应是“内存明明有富余系统还拼命用 swap是不是内核出了问题”第二反应就是赶紧swapoff -a把 swap 关掉。这个操作不是不行但前提错了后果可能很糟。我处理过好几次类似场景想先把结论放前面swap 占用高和物理内存空余在大多数 Linux 系统上是可以同时存在的甚至算正常现象。真正需要紧张的不是 swap 用了多少而是换入换出的速率、MemAvailable 的余量以及谁把页留在了 swap 里。这篇文章就把“swap 高 内存空余”的排查和处置思路完整拆一遍适合被这类告警困扰过的运维、后端开发、服务器管理员参考。1. 收到告警后先做三项检查确认这是“假警报”还是真问题很多告警系统监控swap used / swap total超过 80% 就报“swap 使用率过高”。这个指标本身没有错但它完全不看物理内存的现状也不看 swap 是否有实际读写所以误报率极高。登录机器之后不要急着执行清理命令先看下面三项。1.1 内存真实水位要看 MemAvailable不是只看 Memory Freefree -h默认展示的used列很容易误导人因为它计算的是“已经分配出去的物理内存”其中包含大量 page cache文件页缓存。page cache 缓存的是磁盘文件内容属于内核认为“随时可以回收”的内存。换句话说used高不代表系统缺内存MemAvailable才是内核估算的、新进程发起内存申请时真正能拿到的可用内存。正确的查法是在/proc/meminfo里看两项cat /proc/meminfo | grep -E MemTotal|MemFree|MemAvailableMemFree只是完全空闲的物理页参考价值不大。MemAvailable是内核综合考虑可回收 page cache、可回收 slab、空闲页之后给出的评估值这才是判断“物理内存还有没有余量”的核心标准。如果MemAvailable剩余很大说明内核认为新内存请求可以被满足swap 高只是历史遗留如果MemAvailable已经很低那即使MemFree显示几 GB也说明系统处于内存紧缺边缘迟早继续触发换出。还可以顺手看一眼内存水位/proc/zoneinfo里有low和high水位参数。当空闲页低于low时内核的 kswapd 线程会开始回收内存当高于high时停止。这个文件输出很长只看每个 zone 的 “pages free 是否低于 low” 即可。实际操作里我更推荐用vmstat 1 5观察是否存在持续的内存扫描或者用sar -B看pgscank、pgscand。不过在初始阶段记住一条经验MemAvailable 充足 swap 占用高大概率是假警报。1.2 swap 的“热度”比占用值更重要swap 占用高可能只是“冷数据”躺在里面不代表系统在持续换页。判断热度最直接的方式是看 swap 换入换出计数grep -E pswpin|pswpout /proc/vmstatpswpin表示从 swap 换入内存的页数。pswpout表示从内存换出到 swap 的页数。两个数字都是系统启动以来的累计值只看一次没有意义需要间隔几秒或几分钟采样两次计算差值。更顺手的是用vmstat 1 5看siswap in和soswap out列vmstat 1 5如果si和so基本是 0说明当前没有进程在频繁读写 swapswap 里的页就像躺在仓库里的旧货没被碰过。这时 swap 使用率高多半不是性能瓶颈。反过来如果si每秒几百 KB、so也在持续跳动说明内存压力依然存在甚至有进程正在和 swap 反复“搏斗”这才是需要尽快处理的信号。另外建议看一眼/proc/meminfo里的SwapCached。SwapCached 表示“已经从 swap 读回内存、但还没有从 swap 空间删除”的页数。如果 SwapCached 比较高且在增长说明有进程正在按需换入真正的热点页这事还“热”着不要急着判定为历史残留。1.3 趋势和触发条件决定后续操作单看一个时间点的快照最多只能知道“现在怎么样”无法判断为什么 swap 会变成这样。所以排查时必须结合时间线这台机器最近有没有跑批任务、编译任务、批量启动容器告警是昨天开始的还是最近半小时突然涨上去的swap 使用率涨到高位时物理内存水位是什么情况有没有进程重启、负载突增、连接数暴涨之类的事件我见过很多“swap 高”其实是凌晨定时任务把内存打爆后留下的痕迹。任务跑完了内存释放了但换出的匿名页如果有几十 GB 没有被访问swap 就一直维持高位。这种场景属于“历史包袱”技术上说不需要立刻清理但因为之后系统随时可能再次分配匿名页也建议按后续章节讲的方式做一次主动回收。2. 为什么物理内存还有空余swap 却不肯释放——回收机制的真实逻辑要理解这个现象得先纠正一个常见的错误认识很多人以为 Linux 有某种“后台任务”会定期检查 swap一旦发现物理内存空余就自动把 swap 里的页搬回内存。实际上内核没有这种全局换回的机制。swap 的写入是被动发生的换回也是按需发生的。2.1 换出是被动的换回也是按需的Linux 的物理内存分成两类页文件页file-backed pages比如 page cache、buffer cache回收时直接丢弃或写回文件就行成本低。匿名页anonymous pages比如进程的堆、栈、私有数据回收时必须先写入 swap 才能腾出物理内存。当系统内存压力上升比如有大型任务申请内存或者水位跌破阈值内核的 kswapd 线程或分配路径上的 direct reclaim 会开始扫描 LRU 链表选择冷页回收。匿名页被选中后会被写进 swap 分区对应的物理页释放。关键在这里匿名页换出之后物理内存的空余是腾出来了但换出的页如果没有任何进程再次访问它就一直躺在 swap 里。进程再次访问时CPU 会触发缺页异常内核才去 swap 里读取这个页这称为按需换入。没有人碰它就没人把它搬回来。所以“物理内存还有空余swap 却一直占着”完全符合内核设计逻辑不是 bug。这个逻辑可以用仓库类比仓库swap里的货物只是因为店面物理内存紧张时被挪过去的。后来店面空出来了但只要没人下单取货货物就不会被搬回来。仓库一直占着不代表店面临近崩溃。2.2 swappiness 管的是换出倾向不是自动换回开关vm.swappiness是 Linux 里被误解最多的参数之一。默认值是 60范围是 0 到 100。网上很多老文章说“设为 0 就能禁用 swap”这个说法在今天的内核上已经不对了。较新的内核里swappiness 只影响 kswapd 扫描时对匿名页和文件页的回收偏好值越低越倾向于回收文件页而不是匿名页值越高越倾向于把匿名页换出到 swap。它不控制任何“自动换回”动作所以调低 swappiness 不会让现有 swap 占用降下来只能降低后续继续换出的概率。还有一个容易踩的坑系统可能被 tuned、cloud-init 或发行版默认配置覆盖。比如某些发行版启用了 tuned 的吞吐量性能模板里面的vm.swappiness可能被改成 20 或 30。你手动执行sysctl -w vm.swappiness10当时有效但 tuned 服务在 profile 切换或重启后可能又改回去。因此修改之后要确认有没有 tuned 这类服务在管理参数否则持久化配置只是一张空头支票。2.3 瞬时峰值造成的“历史包袱”往往就是元凶每次内存瞬间吃紧都会有一批匿名页被换出。如果后续内存压力很快消失这些页就停留在 swap 里变成了历史包袱。最容易触发这种场景的操作是大规模编译比如并行 make 或 Maven 构建会瞬间申请大量堆内存。批量启动容器或 JVM 服务同时吃内存。数据库大批量查询、报表任务、ETL 任务。服务器搬迁或服务批量重启。我在模拟项目 X 的环境里见过一个典型案例一台 32 GB 内存的服务器每天晚上跑批处理任务时物理内存会冲到 90% 以上swap 会被写进 6~8 GB任务结束后内存水位回到 50%但 swap 使用率仍然显示 60% 以上。表面上看很吓人实际上任务进程退出后那些换出的页面从来没被重新访问过si、so全是 0。后来我们把监控告警从“swap 使用率超过 80%”改成“swap 写入速率超过 5000 页/秒且持续 10 分钟”假警报才彻底消失。3. 一步步找出 swap 里的“大户进程”从 proc 到 cgroup 的排查套路虽然“swap 高 内存空余”多数是良性现象但你不能直接下结论。万一某个进程因为内存泄漏已经把自己的页面大量换到 swap 里它会表现为内存没有释放而你只看物理内存空余很容易忽略这个隐患。所以排查进程级 swap 占用是必要的一步。3.1 用 VmSwap 扫一遍 /proc找到真正的“常住客”Linux 在/proc/[pid]/status里提供了VmSwap字段单位是 kB直接显示该进程当前有多少匿名页位于 swap。全系统扫描可以用下面这个脚本for f in /proc/[0-9]*/status; do [ -r $f ] || continue name$(grep ^Name: $f | awk {print $2}) swap$(grep ^VmSwap: $f | awk {print $2}) if [ -n $swap ] [ $swap -gt 0 ]; then echo -e $swap\t$name\t${f#/proc/} fi done | sort -rn | head -20输出类似9521024 mysqld 22145 2048120 java 31289 1048576 python3 41201看到这里你可能会想mysqld 占用了差不多 9 GB swap这可是大事。注意别急着恐慌先分清两种可能进程确实把大量不常用内存页换出了这在数据库中很常见。MySQL、PostgreSQL 等进程会把 buffer pool 之外的一些冷数据页放到 swap。swap 里的页面如果之后被频繁访问会造成明显的si峰值这种需要处理。也可以安装smem工具smem -r -s swap它能同时展示 USS、PSS、RSS 和 swap比手写脚本更直观但小系统可能没有这个包手写/proc扫描完全够用。3.2 用 swap 换入换出速率判断真假热闹光找到 VmSwap 大户还不够因为大户也可能只是历史存量。同一时刻收集si、so和 VmSwap能组成比较完整的结论so持续高 VmSwap 增长系统还在持续把内存页写进 swap说明物理内存压力仍然存在。si持续高 VmSwap 高位有进程在反复读取已经换出的页既会产生磁盘 I/O也会造成延迟上升这是性能杀手。si、so均为 0 VmSwap 高位冷数据躺在 swap 里目前对性能没有任何实际影响。其实放到具体进程上可以用/proc/pid/status里的VmSwap变化趋势判断。第一次采样记录值隔 30 秒再采一次如果两次基本一样说明这个进程没有在大量换入换出。如果 30 秒内 VmSwap 下降明显说明进程正在读回页面需要特别关注。3.3 别忘了容器和 cgroup 内部的 swap容器场景和裸机不太一样。进程在容器里看到的/proc/meminfo是宿主机的全局统计数据但它实际能使用的内存受 cgroup 限制。排查顺序就不能只看进程 VmSwap还要看 cgroup 的 swap 统计文件。在 cgroup v2 环境下相关路径是cat /sys/fs/cgroup/memory.swap.current cat /sys/fs/cgroup/memory.swap.max如果第一行的当前值持续接近第二行的最大值说明容器正在被 swap 配额约束即使宿主机物理内存还有空余这个容器也随时可能因 swap 配额不足产生回收行为。使用 Docker 时可以先看docker stats的 SWAP 列再用docker inspect查MemorySwap相关的限制配置。需要警惕的配置是--memory和--memory-swap的组合--memory-swap在容器里的含义是“内存 swap 总上限”不是“单独 swap 上限”。很多容器运维把这两个值配错导致容器内应用刚要使用更多物理内存时直接被内核换出到宿主机 swap表现就是宿主机的 swap 使用率被某个容器拉起来了。4. 想清理 swap先分清楚这四种不同情况再动手这一步才是决策区。不同的确认结果对应不同的动作不能一概而论。4.1 手动回收swapoff 前的安全检查清单如果确认系统当前内存充足且 swap 里的多为冷数据可以考虑把 swap 里的页主动搬回内存操作就是最经典的swapoff -a swapon -aswapoff会把当前 swap 里所有可换回的页全部读回物理内存。如果物理内存不够这个命令会直接失败如果勉强够但剩余内存太少成功后系统反而会再次进入内存紧张状态等于白折腾一次。所以执行前建议按下面清单检查# 1. 查看当前 swap 用量 free -h | grep Swap # 2. 查看 MemAvailable确保远大于 SwapUsed cat /proc/meminfo | grep MemAvailable经验上我要求MemAvailable至少是“swap 当前使用量 2 GB”以上才执行整盘 swapoff。如果差一点可以先释放文件页缓存来提升 MemAvailablesync echo 1 /proc/sys/vm/drop_caches注意drop_caches只清理 page cache不会帮你把匿名页从 swap 搬回来但它能腾出可回收的文件页给 swapoff 创造内存余量。这个操作会短暂降低文件缓存命中率仅建议在明确需要时使用。另外swapoff 会把整台机器卡在“换回阶段”可能持续几分钟到几十分钟期间磁盘 I/O 会拉高应用延迟会上升。必须在维护窗口做别在业务高峰直接敲。4.2 调低 swappiness让系统“以后少写 swap”如果确认问题是系统频繁把匿名页换出而不是历史残留那调整 swappiness 是比反复 swapoff 更有效的做法。sysctl -w vm.swappiness10 echo vm.swappiness10 /etc/sysctl.conf sysctl --system数值怎么选我的建议普通服务器10 到 20 都合适既能避免 swap 频繁写入又不会让系统在极端情况下连最后一点回收弹性都没有。数据库、缓存、搜索类服务常见做法设为 1比如很多 Elasticsearch 部署指南就这么要求。做法是“极度优先保留匿名页”因为这类服务对内存延迟极其敏感宁可尽可能少换出。不建议设 0。前面说过现代内核里 0 不能保证完全禁用换出反而可能让文件页缓存被大量回收导致后续磁盘 I/O 更活跃。很多“0 禁用 swap”的说法是老黄历了。修改之后必须检查是否被 tuned 覆盖tuned-adm active如果系统有 tuned 并且把参数托管了你需要用tuned-adm profile切换到写入了vm.swappiness10的 profile或使用系统管理工具统一推送。否则下次 profile 重新加载配置又会被刷回去。4.3 应用层减灾数据库、缓存类服务怎么配同一台机器上的服务类型决定了 swap 问题的严重程度。以模拟项目 X 的系统为例我们当时发现 swap 里最大的占用来自数据库进程但进程确实已经产生了大量冷页。这种场景单纯调内核参数还不够要从应用侧一起配合。应用类型常见问题推荐处理方向数据库buffer pool 或共享内存之外的内存页被换出调整连接池、缓冲池大小开启内存锁定或合理加大共享内存应用层减少对匿名页的依赖Java 服务JVM 堆内存频繁换出检查堆参数设置避免启动时申请过大堆但长期不用必要时使用 mlock 类能力锁定热内存段计算/批处理任务瞬时大量申请内存后释放任务时段错开或使用内存控制组限制峰值避免打穿物理内存容器平台单容器 swap 配额过高拉高宿主机使用率收紧MemorySwap让容器优先使用物理内存上限而不是把问题传导到宿主机需要注意的是内核参数和业务参数不是非此即彼。比如数据库场景可以同时把 swappiness 调低并适当缩小进程内存缓存Java 场景先调整堆大小观察 VmSwap 是否回落再决定要不要动内核参数。4.4 治本方案监控换入换出速率、查内存泄漏、合理规划容量处理完眼前告警之后长期来看要治的是“为什么会产生这种 swap 峰值”。几个方向可以参考监控指标换掉不要只用 swap 使用率告警核心指标应该是si、so或pswpin/pswpout的速率。只有发现这些速率持续异常时才进入人工排查否则 swap 使用率只是一个辅助参考。查内存泄漏如果一个进程的 VmSwap 和 RSS 一起持续增长且永不回落大概率是堆或缓存泄漏。等 swap 被填满接下来就是 OOM。容量规划物理内存和 swap 的大小关系要有意识。swap 分区过大会掩盖内存不足的问题swap 过小在真实突峰时又会直接触发 OOM。对于服务端常规工作负载swap 通常不超过物理内存的 0.5 到 1 倍特殊场景另行设计。如果开启了 zram 或 zswap这些技术把 swap 做在内存压缩层里free显示的 SwapUsed 不能直接等同于磁盘 swap 占用排查时需要用zramctl或cat /proc/swaps确认 swap 设备的真实类型。之前我在某套桌面级容器环境里就见过swap “占用高”实际是 zram 压缩后的内部数据根本没有磁盘 I/O。5. 一次完整处理实录从告警确认到恢复静默写了这么多理论最后放一条真实处理链路。为了不涉及具体业务这里用“模拟项目 X”代指那套系统。场景一台 32 GB 内存、15 GB swap 的服务器监控平台报 swap 使用率 82%但物理内存剩余约 19 GB。5.1 现场快照与技术判断登录后的第一轮检查结果如下$ free -h total used free shared buff/cache available Mem: 31Gi 11Gi 18Gi 1.2Gi 2.1Gi 19Gi Swap: 15Gi 12Gi 2.9Gi $ vmstat 1 5 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 2 0 12103120 18123456 2123456 102345 0 0 134 12 223 456 1 1 99 0 0 1 0 12103120 18123456 2123456 102345 0 0 120 8 212 434 1 0 99 0 0可以看到si、so都是 0说明 swap 没有任何活跃读写。available还有 19 GB当前没有内存压力。swpd12 GB说明历史上确实发生过大量换出。综合判断这是典型的历史包袱型 swap 占用当前进程没有实际被 swap 读写拖累。为了确认是哪些进程留下的运行了 VmSwap 扫描脚本发现最大的几个来自频繁重启的批处理 Java 进程而这些进程现在已经退出只留下了一堆孤儿 swap 页。5.2 执行清理与验证因为 MemAvailable (19 GB) 远大于 swap used (12 GB)满足安全条件就在维护窗口执行了整盘回收sync echo 1 /proc/sys/vm/drop_caches swapoff -a swapon -a执行过程中观察vmstat的so短暂上升因为 swapoff 正在把页面搬回内存但峰值也在承受范围内。大约 3 分钟后完成再查一次$ free -h total used free shared buff/cache available Mem: 31Gi 27Gi 2.1Gi 1.2Gi 2.1Gi 4.5Gi Swap: 15Gi 0B 15Giswap 被清空但注意物理内存从 19 GB 可用变成了 4.5 GB 可用。这说明之前从 swap 搬回来的 12 GB 页确实占了物理内存。如果这些页面未来再也不被访问它们只会继续占用内存直到内存在其他压力下再次被换出。所以清理 swap 并不是“把垃圾清走了”更像是“把仓库里的货全部搬回了店面”物理内存余量会被吃掉一大块。这也是为什么我反复强调不要在内存余量不足时执行 swapoff。5.3 后续观察与长效机制清理完之后我们没有立刻完事。接着做了三件事把vm.swappiness从默认 60 调整到 10并确认这台机器没有 tuned 覆盖。把告警规则从“swap 使用率超过 80%”改为“so 速率超过 2000 页/秒且持续 5 分钟”。持续观察一周。一周后 swap 使用率稳定在 1% 左右si、so依旧接近 0没有再出现假警报。这次处理说明一个很现实的问题很多 swap 高占用根本不是故障而是监控策略太粗糙造成的“认知性故障”。只有把指标度量对了后续决策才不会被带偏。我自己也踩过几次坑最后分享一个实用心得处理 swap 问题永远把“换入换出速率”放在首位把“使用率”放在次要位置。Linux 的 swap 机制设计出来就是为了吸收内存震荡只要它没有被高频读写占用高一点完全不用焦虑。真正要防的是那种明明si、so持续跳动swap 还不断增长的状态那说明物理内存已经长期透支光调参数救不回来得从应用内存占用和容量规划上动刀了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑