2026/10/16 13:37:45

kswapd0 占用过高怎么排查?Linux 内存回收与 swap 实战复盘

kswapd0 占用过高怎么排查?Linux 内存回收与 swap 实战复盘 1. 从一次深夜告警说起kswapd0 到底是什么角色凌晨两点收到监控告警某台线上服务器的内存使用率飙到 92%负载也跟着往上窜但业务 QPS 并没有明显波动。登上机器第一件事就是top排在最前面的不是哪个业务进程而是一个叫kswapd0的内核线程CPU 占用长期在 20% 到 40% 之间跳动。这个场景我相信做过 Linux 运维或者后端开发的同学多少都遇到过它不像 OOM 那样直接把你进程干掉但会像慢性病一样持续拖慢整台机器。先把概念说清楚。kswapd0是 Linux 内核内存管理子系统里的一个内核态换页线程每个 NUMA 节点会有一个编号从 kswapd0 开始。它的职责非常单一当系统物理内存紧张、空闲页低于某个水位线watermark时内核唤醒它让它去回收内存页——把匿名页换出到交换分区swap把文件页page cache丢弃或回写。你可以把它理解成公司里的后勤回收员平时不显山不露水一旦仓库物理内存快满了它就开始疯狂整理、往外搬东西。问题在于这个后勤一旦长期高强度工作说明系统内存已经处于持续紧张状态。它本身不是病根而是症状。很多人排查时第一反应是能不能把 kswapd0 干掉或者限制它这是典型的治标不治本——它是内核线程你 kill 不掉也不该 kill。真正要做的是找到为什么内存一直不够用然后从根上解决。这篇内容我会完整复盘一次 kswapd0 内存过高的排查链路从现象确认、指标采集、根因定位到最终修复和验证把每一步的判断依据和踩过的坑都讲清楚。适合有一定 Linux 基础、正在被内存问题困扰的运维和后端同学参考也适合想搞懂 Linux 内存回收机制的朋友当作一次实战案例来看。2. 先别急着动手确认 kswapd0 高占用是真问题还是假象2.1 区分瞬时抖动和持续高压我见过不少人一看到 kswapd0 出现在 top 前列就慌了其实要分情况。系统在跑一些大文件读写、日志轮转、备份任务时page cache 会快速膨胀内核触发一次内存回收kswapd0 短暂冲高几十秒然后回落这属于正常行为不用管。真正需要警惕的是它持续占用 CPU比如连续十几分钟都在 10% 以上同时伴随siswap in、soswap out持续非零。判断方法很简单用vmstat看一段时间的趋势vmstat 1 10重点看几列si和so是换入换出速率单位 KB/s如果长期大于 0说明内存在物理层面已经不够用了正在拿磁盘当内存使free列是空闲内存buff/cache是缓存。如果si/so一直是 0只是 kswapd0 偶尔冒头那大概率是文件页回收不用太紧张。2.2 用 sar 拉历史数据别只看当下现场top只能看到此刻但内存问题往往是周期性的。我习惯先拉一段历史数据sar -r -f /var/log/sa/saXX sar -W -f /var/log/sa/saXX-r看内存和 swap 使用趋势-W看换页速率。这样能判断出问题是一直高还是每天某个时段高。我这次遇到的案例就是每天凌晨备份任务跑起来后内存开始爬升白天又慢慢回落属于典型的周期性内存压力如果只看白天现场很容易误判成没问题。2.3 确认 swap 的配置状态在动手之前必须搞清楚这台机器有没有开 swap、开了多大free -h swapon --show cat /proc/sys/vm/swappiness这里有个关键点如果 swap 是 0那 kswapd0 高占用反而更危险因为它只能靠回收 page cache 和直接回收direct reclaim来腾内存一旦 cache 也回收不动就会触发 OOM Killer 直接杀进程。如果 swap 很大kswapd0 会频繁换页虽然不杀进程但磁盘 IO 会被拖垮业务延迟飙升。两种情况的处理思路完全不同所以这一步不能省。提示swappiness默认值通常是 60表示内核比较积极地使用 swap。对于数据库类、延迟敏感型服务一般建议调到 1 到 10减少不必要的换页对于普通应用服务器保持默认或略低即可。这个值不是越小越好设成 0 在部分内核版本上会完全禁用匿名页换出反而容易触发 OOM。3. 定位真凶从内存分布一路挖到具体进程3.1 先看整体内存账本确认了 kswapd0 确实在持续工作后下一步是搞清楚内存到底被谁吃了。我一般按这个顺序看free -h cat /proc/meminfofree给的是概览/proc/meminfo才是明细。重点看这几个字段字段含义排查价值MemTotal物理内存总量基线MemFree完全空闲内存通常很小正常MemAvailable可用内存估算最关键低于总量 10% 要警惕Buffers块设备缓冲一般不大Cached文件页缓存大不一定坏可回收SwapCached换出又被换回的内存高说明换页频繁Active/Inactive活跃/非活跃页判断回收压力Dirty待回写脏页高会拖慢回收我这次案例里MemAvailable只剩 800MB总内存 16GBSwapCached有 2GB 多Dirty长期在几百 MB基本可以确认是真实内存不足 脏页回写压力双重叠加。3.2 按进程排序找出内存大户整体账本看完就要落到具体进程。top按M排序只是入门我更推荐用ps精确排序ps aux --sort-%mem | head -20但这里有个坑ps里的%MEM是按 RSS 算的而多个进程共享的内存比如共享库会被重复计算容易高估。更准确的方式是看PSSsmem -tk -s pss如果机器上没有 smem可以用/proc/pid/smaps手动加但比较麻烦。我一般先用ps圈定嫌疑进程再用pmap看它的内存段分布pmap -x pid | tail -203.3 别忘了内核自己也在吃内存很多人排查内存只盯着用户态进程忽略了内核态。slab缓存、内核模块、网络缓冲区都可能吃掉大量内存slabtop -s c cat /proc/meminfo | grep -i slab我遇到过好几次 kswapd0 高占用最后发现是某个内核对象比如 dentry、inode 缓存异常膨胀。这次案例里SReclaimable有 1.5GB属于正常范围所以排除了 slab 泄漏的可能。3.4 用 pidstat 看换页行为定位到嫌疑进程后还要确认它是不是真的在频繁换页pidstat -r -p pid 1 10看majflt/s主缺页需要从磁盘读和minflt/s次缺页内存内分配。如果majflt/s很高说明这个进程的内存被换出后又频繁换回是 kswapd0 高占用的直接推手。我这次锁定的那个进程majflt/s稳定在 200 以上基本实锤。4. 根因分析为什么内存会持续不够用4.1 内存泄漏最隐蔽也最常见找到内存大户只是第一步关键是判断它是正常占用大还是泄漏。判断方法就是长时间观察 RSS 趋势while true; do ps -o pid,rss,cmd -p pid /tmp/mem_trend.log sleep 60 done跑几个小时甚至一天如果 RSS 只涨不跌且业务量没有对应增长那基本就是泄漏。我这次案例里那个进程 RSS 从 2GB 一路涨到 6GB中间没有任何回落典型的泄漏特征。后来查代码发现是一个缓存对象只增不删没有设置过期淘汰策略。4.2 缓存配置不合理把内存当仓库用有些应用比如自研的本地缓存、某些中间件的 buffer会默认申请很大一块内存或者按能用多少用多少的策略吃内存。这类问题不算泄漏但同样会把系统逼到 swap。判断方法是看进程启动参数和配置文件里的内存相关配置项比如 JVM 的-Xmx、缓存的max-size、连接池的max-active等。我踩过的一个坑某服务 JVM 堆设了 8GB但机器只有 16GB加上堆外内存、元空间、线程栈实际占用轻松超过 10GB再叠加其他服务内存必然紧张。堆内存不是设得越大越好要留足给操作系统、page cache 和其他进程的空间。4.3 突发流量或批处理任务周期性压力前面提到我这次是凌晨备份任务触发的。这类问题的特点是可预测每天固定时间内存爬升任务结束又回落。排查时要结合 crontab、定时任务列表、业务日志的时间线一起看crontab -l ls -l /etc/cron.d/ grep -r backup\|dump\|export /etc/cron* 2/dev/null找到那个元凶任务后要么错峰执行要么限制它的资源占用用nice、ionice或 cgroup。4.4 内存碎片与 NUMA 不均衡这个属于进阶问题。有时候free看总空闲内存还有不少但 kswapd0 依然很忙可能是内存碎片导致大块连续内存分配不出来或者NUMA 节点间内存分布不均某个节点压力特别大。检查方法cat /proc/buddyinfo numactl --hardware cat /proc/zoneinfo | grep -A5 Node如果buddyinfo里高阶块order 大的几乎为 0说明碎片严重可以考虑在业务低峰期做内存规整或者调整应用的内存分配策略。5. 修复方案从应急止血到根治5.1 应急止血先让业务稳住线上出问题第一优先级是恢复服务不是找根因。我一般按这个顺序操作临时扩容 swap如果磁盘 IO 扛得住创建一个 swap 文件顶一下给排查争取时间。fallocate -l 4G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile重启泄漏进程如果确认是某个进程泄漏且重启代价可接受直接重启它内存立刻释放。这是最快的手段但要评估重启对业务的影响。调整 swappiness 降低换页频率如果 swap 使用本身不是问题只是换页太频繁可以临时调低sysctl vm.swappiness10注意应急操作只是争取时间一定要在业务恢复后继续做根因分析和长期修复否则下次还会复发。我见过太多团队重启大法用上瘾最后问题越积越多。5.2 根治泄漏改代码或加限制如果根因是内存泄漏那就得从代码层面解决。常见手段包括给缓存加 LRU 淘汰和 TTL、修复未释放的资源句柄、优化数据结构减少冗余。如果短期改不了代码可以用 cgroup 给进程设内存上限超了让它自己 OOM 重启至少不拖垮整机# 使用 systemd 限制内存 systemctl set-property service MemoryMax4G5.3 优化配置把内存账算清楚对于配置不合理的情况要重新做容量规划。我的经验是留出至少 20% 的内存给系统和 page cache剩下的再分配给应用。比如 16GB 的机器应用总内存控制在 12GB 以内比较稳妥。JVM 类应用还要注意堆外内存-Xmx加上 Metaspace、线程栈、DirectByteBuffer实际占用往往比堆大 20% 到 30%。5.4 错峰与限流对付周期性压力对于备份、导出这类批处理任务最有效的办法是错峰。把凌晨 2 点的任务挪到业务最低谷的 4 点或者用nice/ionice降低它的优先级nice -n 19 ionice -c3 /path/to/backup.sh这样即使它吃内存也不会和核心业务抢资源。6. 验证与复盘怎么确认问题真的解决了6.1 观察关键指标回落修复后不能只看感觉好了要用数据说话。我一般盯这几个指标至少 24 小时vmstat的si/so是否回到 0 附近kswapd0的 CPU 占用是否降到 1% 以下MemAvailable是否稳定在总量的 20% 以上业务 P99 延迟是否恢复正常6.2 建立监控告警这次问题暴露出来的一个短板是告警滞后。原来的告警只监控了内存使用率没监控 swap 换页速率和 kswapd0 的 CPU。后来我补了几条规则si/so持续 5 分钟大于 100KB/s 告警、kswapd0 CPU 持续 10 分钟大于 5% 告警、MemAvailable低于 15% 告警。这样能在问题恶化前就介入。6.3 复盘要落到机制而不是人每次故障复盘我最怕听到下次注意点。真正有价值的复盘是把它变成机制比如给所有服务加上内存上限、把内存趋势纳入日常巡检、给批处理任务统一加资源限制。这样即使换了人问题也不会重演。7. 几个容易踩的坑和我的实操心得第一个坑是盲目加内存。很多人一遇到内存问题就申请扩容但如果根因是泄漏加多少内存都会被吃光只是把爆炸时间往后推。我建议先定位根因确认是容量问题再扩容。第二个坑是把 swappiness 设成 0。网上很多优化教程说设成 0 能提升性能但在某些内核版本上这会导致匿名页完全无法换出内存压力大时直接触发 OOM。稳妥的做法是设成 1 到 10而不是 0。第三个坑是只看 RSS 不看 PSS。共享内存多的场景下RSS 会严重高估单个进程的占用导致误判。用 smem 或者/proc/pid/smaps_rollup看 PSS 更准。第四个坑是忽略 page cache 的价值。看到Cached很大就以为内存被浪费了其实 page cache 是可回收的能大幅提升 IO 性能。真正要关注的是MemAvailable它已经把可回收部分算进去了。最后一个心得内存问题一定要看趋势不能看快照。单次free或top能给你的信息非常有限只有把时间维度拉长才能区分正常波动和真实异常。我现在排查内存问题第一步永远是拉历史监控而不是登机器敲命令。这个习惯帮我省了无数次误判。