Linux 服务器内存使用与调优

Linux 内存管理是服务器运维中最容易被误解、也最容易导致严重故障的领域之一。 很多人看到 free -h 里 used 很高、free 很低就紧张,以为内存要爆了,然后疯狂杀进程、加 swap、调 swappiness,结果系统反而更卡;也有人看到大量 cache/buffer 就觉得“内存浪费了”,手动 drop_caches 清缓存,结果数据库、文件系统性能瞬间暴跌。

本文从真实生产视角出发,系统讲解 Linux 内存使用的正确理解方式、关键指标解读、常见误区、调优思路与优先级,帮助你建立一套“看懂内存 → 判断是否真缺 → 合理调优”的完整方法论。

一、Linux 内存使用的正确认知(先搞清楚这几点)

Linux 内核的内存管理哲学可以用一句话概括:

“空闲内存是浪费的资源”

内核会尽可能把所有空闲内存用作:

  • page cache(文件缓存)
  • buffer cache(块设备缓冲)
  • slab 缓存(inode/dentry/kmalloc 等内核对象)
  • 各种匿名映射、共享内存等

因此,free -h 里看到的 free 列 几乎永远都很小,真正能用的内存其实是 available 列

关键指标对照表(生产最常用)

指标含义关注阈值(物理内存占比)解释与建议
Mem: available真正可分配给新进程的内存(最重要)< 10–15% 报警 < 5% 严重低于此值开始出现压力
Mem: used已分配给进程 + 内核使用的内存不要只看这个
Mem: buff/cachepage cache + buffer + slab越高越好这是“好事”,可回收
Swap: used已使用的交换分区> 0 开始关注 > 10% 严重持续换页 = 内存真不足
si/so (vmstat)每秒 swap in / swap out> 几十 KB/s 持续就问题换页速率是关键
kswapd0 / khugepaged内核内存回收线程 CPU 占用持续 > 5–10% 说明压力大内存回收吃 CPU

一句话总结内存是否紧张,看 available + si/so + kswapd0 CPU,而不是 used 或 free

二、常见内存使用模式与判断标准

1. 健康状态(最理想)

  • available > 20–30%
  • swap used = 0 或极少
  • buff/cache 占 40–70%
  • kswapd0 几乎不吃 CPU
  • 业务延迟稳定,无明显抖动

2. 轻度压力(需关注)

  • available 10–20%
  • swap used < 5%,偶尔有少量 si/so
  • kswapd0 偶尔小幅波动

此时无需立即干预,但应开始排查:

  • 是否有内存泄漏进程
  • 是否可以减少常驻内存(Java heap、Redis maxmemory、MySQL innodb_buffer_pool)
  • 是否可以加物理内存

3. 中度压力(必须优化)

  • available < 10%
  • swap used 5–20%,si/so 持续几十 KB/s
  • kswapd0 占用 5–20% CPU

此时系统已经开始出现延迟抖动,必须立即行动。

4. 严重压力(系统濒临崩溃)

  • available < 5% 或 几百 MB
  • swap used > 30–50%
  • si/so 几 MB/s 以上
  • kswapd0 / khugepaged 吃 30%+ CPU
  • OOM killer 开始杀进程(dmesg 有 Out of memory 记录)

此时通常已经出现明显的业务超时、连接堆积、甚至系统假死。

三、内存调优的优先级与高收益方向

最高收益(必须先做)

  1. 定位内存泄漏与驻留大户
    • ps -eo pid,rss,vsz,cmd --sort=-rss | head -20(RSS 常驻内存排序)
    • smem -t -k 或 smem -t -k -u(更准确的用户/进程内存统计)
    • Java:jmap -histo、jstat -gcutil、VisualVM、jcmd GC.heap_dump
    • Redis:INFO memory,检查 used_memory 与 maxmemory
    • MySQL:SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_%'
  2. 合理设置进程内存上限
    • cgroup v2:systemd 或 docker compose 中 MemoryMax
    • ulimit -v(虚拟内存限制)
    • Java:-Xmx < 物理内存的 50–75%(留给 page cache)
  3. 关闭或减少不必要的常驻服务
    • 关闭透明大页(transparent hugepage = never/madvise,数据库/Redis 推荐)
    • 限制 slab 缓存膨胀(slabtop 查看,必要时 echo 3 > /proc/sys/vm/drop_caches 临时清理)

中等收益(场景依赖)

  1. 调整 swappiness 与脏页回写
    • vm.swappiness = 1–10(服务器几乎不建议换出匿名页)
    • vm.dirty_ratio = 5–10%(减少脏页堆积)
    • vm.dirty_background_ratio = 2–5%(后台开始回写)
  2. NUMA 优化(多路服务器)
    • numactl --cpunodebind=0 --membind=0 绑定进程到同一 NUMA 节点
    • 避免跨 NUMA 访问内存(延迟增加 1.5–2 倍)
  3. zram / zswap 作为 swap 加速
    • 小内存服务器(< 32GB)推荐开启 zram(压缩 swap 在内存中)
    • 比传统 swap 延迟低 5–10 倍

低收益但常被误调的参数(慎用)

  • vm.overcommit_memory = 1(过度提交,风险高)
  • vm.min_free_kbytes 过大(抢占太多 page cache)
  • vm.zone_reclaim_mode = 1(NUMA 场景可能适得其反)

四、监控与告警建议(生产必备)

必须监控的指标:

  • MemAvailable / MemTotal < 15% → warning
  • SwapUsed > 5% 或 si/so > 100KB/s → critical
  • kswapd0 CPU > 10% 持续 5 分钟 → warning
  • OOM killer 触发(dmesg/journalctl 关键字)→ critical
  • 进程 RSS 增长趋势(prometheus process exporter)

五、总结:内存调优的分层优先级

优化层级典型收益幅度优先级备注
定位泄漏与驻留大户30%–∞★★★★★必须先做
进程内存上限与 cgroup20%–80%★★★★☆必须做
关闭透明大页、调 swappiness10%–50%★★★★☆数据库/Redis 必做
NUMA 绑核与内存节点5%–40%★★★☆☆多路服务器
zram/zswap 启用10%–60%(小内存)★★★☆☆内存紧张时
vm.dirty_ratio 等微调0%–20%★★☆☆☆慎调

一句话总结内存调优的核心不是“让 free 变大”,而是“让 available 保持充足,同时让 page cache 尽可能多地发挥作用”

先用 free -h、vmstat、slabtop、ps -eo rss 看清楚谁在吃内存,再决定是杀进程、加内存、调参数还是优化代码。 把时间花在定位内存大户和设置合理的 cgroup 限制上,远比盲目改 swappiness 或 drop_caches 有效得多。

如果你当前服务器内存压力大,欢迎提供 free -h、vmstat 1 5、top 截图或 slabtop 输出,我可以帮你快速分析最可能的瓶颈点。

THE END