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/cache | page 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 记录)
此时通常已经出现明显的业务超时、连接堆积、甚至系统假死。
三、内存调优的优先级与高收益方向
最高收益(必须先做)
- 定位内存泄漏与驻留大户
- 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_%'
- 合理设置进程内存上限
- cgroup v2:systemd 或 docker compose 中 MemoryMax
- ulimit -v(虚拟内存限制)
- Java:-Xmx < 物理内存的 50–75%(留给 page cache)
- 关闭或减少不必要的常驻服务
- 关闭透明大页(transparent hugepage = never/madvise,数据库/Redis 推荐)
- 限制 slab 缓存膨胀(slabtop 查看,必要时 echo 3 > /proc/sys/vm/drop_caches 临时清理)
中等收益(场景依赖)
- 调整 swappiness 与脏页回写
- vm.swappiness = 1–10(服务器几乎不建议换出匿名页)
- vm.dirty_ratio = 5–10%(减少脏页堆积)
- vm.dirty_background_ratio = 2–5%(后台开始回写)
- NUMA 优化(多路服务器)
- numactl --cpunodebind=0 --membind=0 绑定进程到同一 NUMA 节点
- 避免跨 NUMA 访问内存(延迟增加 1.5–2 倍)
- 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%–∞ | ★★★★★ | 必须先做 |
| 进程内存上限与 cgroup | 20%–80% | ★★★★☆ | 必须做 |
| 关闭透明大页、调 swappiness | 10%–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 输出,我可以帮你快速分析最可能的瓶颈点。
