java系统swap卡顿本质是jvm匿名页被频繁换入换出引发gc延迟抖动,根本原因是内核swappiness默认值60与java内存特征不匹配;生产推荐设为1,配合cgroup限容、大页锁定与memavailable监控实现稳定。

Java 系统在 Linux 上因 Swap 频繁交互而卡顿,本质是 JVM 堆内存被换出到磁盘,GC 时需重新换入,引发毫秒级甚至百毫秒级延迟抖动。这不是“内存够不够”的表象问题,而是内核内存回收策略与 Java 内存使用特征不匹配的结果。
看准现象:Swap 抖动不是“用了 Swap”,而是“正在频繁换入换出”
别只盯着 free -h 里 Swap used 是不是为 0。关键指标是:
-
vmstat 1中的 si(swap-in)和 so(swap-out)列:持续 > 2–5 次/秒即属异常; -
cat /proc/vmstat | grep -E "pgpgin|pgpgout":数值每秒明显上涨(如 pgpgout 增速超 1000); -
cat /proc/PID/status | grep VmSwap:确认 Java 进程自身已有非零 Swap 使用量; - GC 日志中 Full GC 耗时突增(比如从 300ms 跃升至 1200ms+),且
jstat -gc显示FGCT(Full GC 总耗时)占比飙升。
调低 swappiness:让 JVM 内存尽量留在物理内存里
Java 进程大量使用匿名页(堆、元空间、直接内存),这类页无法被文件缓存机制释放,只能 swap 或触发 OOM。默认 swappiness=60 会让内核在还有 3–4GB MemAvailable 时就换出冷 JVM 页——这正是抖动源头。
- 标准生产 Java 服务(如 Spring Boot,堆 4–16G):设为 1;
- 高 SLA 场景(支付、实时风控)或使用 ZGC/Shenandoah:可设为 0(需确保物理内存充足,避免 OOM);
- 混部环境(Java + 其他容器):宿主机设为 1,并用 cgroup v2 硬限 Java 进程组内存(如
echo 12G > memory.max)。
临时生效:sudo sysctl vm.swappiness=1;
永久生效:写入 /etc/sysctl.d/99-java-swappiness.conf,再执行 sudo sysctl --system。
配合基础防护:堵住 Swap 触发的其他路径
单调 swappiness 不够,还需切断常见诱因:
-
检查 JVM 堆是否过大:堆 + 元空间 + 直接内存 + 线程栈 ≈ 不应超过物理内存的 70%。例如 16G 机器,-Xmx8g 是安全起点,若观察到
free -h的 available 长期 -
禁用不必要的后台 Java 服务:Linux 下运行
systemctl list-unit-files | grep java,关闭 Java Update Scheduler 等非核心服务; -
监控真实可用内存:重点关注
MemAvailable(非 free),它已扣除不可回收缓存。可用脚本持续 watch:watch -n 2 'grep -i "memavailable" /proc/meminfo'; - 避免混用机械硬盘做 Swap:若必须启用 Swap,优先用 SSD 创建 swapfile,或干脆用足够内存规避 Swap。
验证是否真正解决
调整后不要只看 Swap used 是否归零,要验证抖动是否消失:
- 继续运行
vmstat 1,确认 si/so 长期稳定在 0 或个位数波动; - 用
grep -i "major" /proc/PID/status查看 major fault(缺页中断)次数,应比调整前显著下降; - 压测或观察线上流量高峰时段的 GC 日志,Full GC 耗时回归稳定区间(如波动 ≤ ±100ms);
- 结合
arthas dashboard或top -H -p PID,确认线程阻塞和 CPU wa(I/O wait)占比不再异常升高。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











