根本原因是vm.swappiness默认值60过高,导致内核过度将匿名页换出至Swap,而非物理内存不足;应根据内存容量将swappiness设为1–10,并用cat /proc/sys/vm/swappiness验证是否生效。

Swap占用高但物理内存还有余量,问题不在容量而在调度策略
宝塔面板 Swap 占用 100% 却不缺物理内存,本质不是“没空间”,而是内核被默认 vm.swappiness=60 推着把匿名页(如 PHP-FPM 堆、Python 对象、BT-Task 的长期缓存)往 Swap 里塞。尤其在 2GB 内存的小 VPS 上,刚 fork 出的子进程可能几秒就被换出,造成响应毛刺,而 free -h 看起来内存还剩 30%——这很典型,也最容易误判。
为什么改 Swappiness 比扩容 Swap 更有效
扩容 Swap(比如从 1G 加到 4G)反而可能让问题更糟:系统更“放心”地换出,BT-Task、Java 进程、MySQL 缓冲区会持续堆积到 Swap,导致 si/so(swap-in / swap-out)持续走高,I/O 延迟上升。真正该做的是降低换出倾向:
- 2GB 及以下内存:设为
10,给活跃页留足回流时间 - 2–4GB 内存:设为
5或10,兼顾突发负载与稳定性 - 8GB+ 且跑 MySQL/Redis:可设为
1;设0不禁用 Swap,只是不主动换出匿名页(文件页仍可换)
临时生效:sysctl vm.swappiness=10;永久生效必须写入 /etc/sysctl.conf 后执行 sysctl -p,否则重启即失效。
宝塔 Linux 工具箱的 Swap 设置根本不管 Swappiness
很多人点了“设置 Swap 为 2G”就以为调优完成,其实工具箱只做三件事:dd 创建 /www/swapfile、mkswap 格式化、写入 /etc/fstab。它完全不碰 vm.swappiness,也不干预 BT-Task 或其他进程的内存行为。你看到 Swap 持续爆满,90% 是因为这个参数还是默认的 60,而工具箱对此毫无感知。
验证方式很简单:cat /proc/sys/vm/swappiness,如果不是你设的值,说明 sysctl -p 没执行,或者写错位置(比如写进了 /etc/sysctl.d/99-custom.conf 却没 reload)。
哪些进程在偷偷吃 Swap?别只看 %MEM
top 的 %MEM 列只反映 RSS(驻留物理内存),对 Swap 占用完全不敏感。真正吃 Swap 的,往往是 RSS 小但 VmSwap 大的长周期后台进程:
-
/www/server/panel/pyenv/bin/python3 /www/server/panel/BT-Task:宝塔任务管理器,备份卡住、日志分析超时后容易泄漏 VmSwap,单进程占 500MB+ Swap 很常见 -
/usr/bin/java -Xmx1024m:JVM 堆外内存或 GC 暂停期间未回收页,也可能大量落 Swap -
php-fpm子进程:swappiness 高时,空闲子进程常被整块换出,唤醒时触发 swap-in 延迟
查真实 Swap 占用用这个脚本(复制粘贴即可):
for proc in $(find /proc -maxdepth 1 -type d -regex "^/proc/[0-9]+"); do pid=$(basename $proc); swap=$(awk '/VmSwap/ {print $2}' $proc/status 2>/dev/null); [ "$swap" != "" ] && [ "$swap" -gt 0 ] && echo "PID: $pid | Swap: ${swap}KB" | sort -k4 -n -r | head -10; done
Swappiness 调低后,Swap 使用不会立刻清零——它需要几分钟让内核逐步把页换回 RAM。别急着 swapoff -a,先等 watch -n1 'free -h' 看 Swap used 缓慢回落,这才是健康释放。强行关 Swap 可能触发 OOM 杀掉 BT-Task 或数据库进程,得不偿失。


















