5 个 Linux 内存调优技巧,第 3 个能帮你省下一台服务器
一、什么时候需要内存调优?
- 系统频繁 OOM(Out of Memory)杀进程
- 响应变慢,
free显示内存耗尽,swap 使用率高 - 数据库或 Java 应用 GC 频繁、停顿长
- 你发现系统 cache 占了几十 G,想知道能不能回收
读完这篇你会:看懂 /proc/meminfo,设置合理的 swap 和缓存回收策略,定位内存泄漏,用大页提升性能。
前置条件:能登录 Linux 服务器,有 root 或 sudo。我用 Ubuntu 22.04 和 CentOS 7 验证过。
二、先把内存指标看懂,别被 free 骗了
很多新手只盯着 free 那一行,发现只剩几百兆,以为内存不够。其实 Linux 会尽量用空闲内存做 cache,cache 是可以回收的。
正确的看法:看 available 列。
free -h
示例输出(32G 内存机器):
total used free shared buff/cache available
Mem: 31G 12G 1.2G 123M 18G 18G
Swap: 2.0G 200M 1.8G
这里 free 只有 1.2G,但 available 有 18G,说明还有大量 cache 可以回收,系统并不紧张。used 12G 包含了进程实际占用 + cache,但 available 已经剔除了可回收部分。
详细看 /proc/meminfo:
cat /proc/meminfo | grep -E "MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Dirty|Writeback"
重点字段:
MemAvailable:估计有多少内存可用于启动新应用(不触发 swap)Buffers:块设备的缓冲区(比如磁盘元数据)Cached:文件缓存(page cache)Dirty:等待写回磁盘的脏页Writeback:正在写回的页
三、swap 调优:别让它拖垮性能
3.1 swap 该设多大?
老教科书说 swap = 2 倍内存,那是内存 512M 时代的经验。现在服务器内存 64G、128G,你给 256G swap?浪费磁盘,还会让系统在内存压力下疯狂换页。
我个人的经验:
- 内存 < 8G:swap = 内存大小
- 内存 8G - 64G:swap = 4G - 8G
- 内存 > 64G:swap = 8G(或者干脆不要,除非跑 Hadoop 等需要 swap 的应用)
3.2 vm.swappiness:什么时候开始用 swap
范围 0-100,默认 60。值越大,越积极用 swap。
对数据库或延迟敏感应用,建议调到 10-20,让它尽量留在内存。
# 临时生效
sysctl -w vm.swappiness=10
# 永久生效
echo "vm.swappiness=10" >> /etc/sysctl.conf
注意:设成 0 不代表禁用 swap,只是极度不愿意换出。内核 3.5 以后,0 的含义略有变化,想彻底禁用 swap 可以用 swapoff -a。
3.3 添加 swap 文件(不用分区)
如果当初没分 swap 分区,可以用文件临时顶:
dd if=/dev/zero of=/swapfile bs=1M count=4096 # 4G
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab
坑:dd 会先写满 4G 零,会占用磁盘 IO。生产环境建议在低峰期操作。
四、缓存回收:什么时候手动 drop cache
Linux 会自动回收 cache,但有时候你想立即释放(比如测试内存泄漏)。不要在生产环境随便 drop,会导致性能抖动。
# 清空 page cache(文件缓存)
echo 1 > /proc/sys/vm/drop_caches
# 清空 dentry 和 inode 缓存
echo 2 > /proc/sys/vm/drop_caches
# 清空全部
echo 3 > /proc/sys/vm/drop_caches
我推荐:只在测试环境或明确知道 cache 占用了大量内存且业务低峰期时用 echo 1。echo 3 会让系统短暂“失忆”,性能骤降。
另外,调 vm.vfs_cache_pressure(默认 100)可以控制内核回收目录/ inode 缓存的倾向。数值越大越积极回收。如果文件系统有很多小文件,可以调到 200,避免 cache 占满内存。
sysctl -w vm.vfs_cache_pressure=200
五、定位内存泄漏:哪个进程在偷吃?
5.1 按 RSS 排序
ps aux --sort=-%mem | head -20
记下可疑 PID。然后持续观察它的内存变化:
watch -n 2 "ps -p PID -o rss,vsz,comm"
如果 RSS 每几分钟涨 100M 且从不下降,基本是泄漏。
5.2 查看进程内存映射详情
cat /proc/PID/smaps | grep -E "Pss|Rss|Size" | head -20
Pss(比例驻留大小)是更准确的内存占用(考虑了共享库)。如果某个进程的 Pss 持续增长,基本确定泄漏。
5.3 用 valgrind 找泄漏(开发环境)
生产环境别用 valgrind,开销太大。开发环境可以:
valgrind --leak-check=full ./your_program
彩蛋:用 pmap -x PID 快速看内存分布,pmap 的输出比 /proc/smaps 更直观。
六、大页内存(HugePages):数据库性能神器
TLB(页表缓存)条目有限,用默认 4K 页,访问大量内存时 TLB miss 频繁。2MB 或 1GB 的大页能显著提升性能(尤其是数据库、DPDK、JVM)。
6.1 配置 HugePages(2MB)
# 查看当前大页配置
cat /proc/sys/vm/nr_hugepages
# 临时分配 1024 个 2MB 大页(共 2GB)
echo 1024 > /proc/sys/vm/nr_hugepages
# 永久:在 /etc/sysctl.conf 添加
vm.nr_hugepages=1024
挂载 hugetlbfs:
mkdir /mnt/huge
mount -t hugetlbfs none /mnt/huge
echo "none /mnt/huge hugetlbfs defaults 0 0" >> /etc/fstab
6.2 透明大页(THP):建议关闭
THP 是内核自动将内存页合并成大页,但容易导致延迟抖动。数据库(MySQL、PostgreSQL、MongoDB)官方都推荐关闭 THP。
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
永久关闭:在 /etc/rc.local 或 systemd service 里加。
七、实战案例:MySQL 内存被 swap 拖死
场景还原
一台 MySQL 服务器,内存 64G,innodb_buffer_pool_size 设了 40G,系统 swap 8G。跑着跑着,MySQL 响应从 1ms 飙到 200ms。
free -h 看到 swap 用了 6G,available 还有 20G。明明有空闲内存,为什么用 swap?
排查
检查 vm.swappiness 默认 60。MySQL 进程内存占用虽然没超物理,但内核认为某些匿名页(比如 InnoDB 的 old blocks)不活跃,换出到 swap。
解决
- 调低
vm.swappiness=10 - 重启 MySQL 让内存重新分布
- 修改 MySQL 配置:
innodb_numa_interleave=1(开启 NUMA 交错,避免内存分配不均)
结果:swap 使用降到了几十 MB,响应恢复正常。
教训:数据库服务器一定调低 swappiness,有条件的话用 numactl 绑定内存节点。
八、彩蛋:几个冷门但有用的内存命令
numactl --hardware看 NUMA 拓扑。跨节点访问内存延迟高,可以用numactl --cpunodebind=0 --membind=0 ./app绑定。slabtop看内核 slab 缓存(比如 dentry、inode 占多少内存)。如果dentry太大,考虑调vfs_cache_pressure。echo m > /proc/sysrq-trigger会输出内存信息到内核日志(别随便敲,会触发 dump)。smem工具(需要安装)可以统计 PSS/USS,比ps更准确。
九、验证调优效果
- 用
stress模拟内存压力:
stress --vm 2 --vm-bytes 1G --timeout 60
- 同时观察
vmstat 1的si和so(swap in/out)。如果si持续大于 0,说明内存不足或 swappiness 太高。 - 对数据库,压测观察响应时间和
%iowait。
十、常见问题(真有人问过我)
Q:free 显示 available 很多,但应用还是 OOM 被杀?
A:OOM 通常因为单个进程超过了 ulimit -v 或 cgroup 限制,不是系统总内存不足。检查 dmesg | grep -i oom 看哪个进程被杀。
Q:/proc/meminfo 里的 MemAvailable 是怎么算的?
A:估计值 = MemFree + Buffers + Cached - 不可回收部分(如共享内存)。不同内核版本算法有差异,但趋势可信。
Q:可以关闭 swap 吗?
A:可以。但强烈建议保留少量 swap,哪怕 1G,因为内核在某些内存压力下需要 swap 做后台回收,否则可能直接 OOM。云上很多实例默认无 swap,自己加文件。
Q:Java 应用 RSS 高但实际堆很小,为什么?
A:JVM 会预分配内存(Xmx),还会加载共享库、线程栈、Metaspace。用 jcmd 或 Native Memory Tracking 分析。
最后总结
- 先看
available,别被free吓到。 - 调低 swappiness(数据库 10-20),避免无辜 swap。
- 内存泄漏用
ps和/proc/PID/smaps跟踪 RSS 趋势。 - 大页提升性能,关闭 THP 避免延迟毛刺。
- NUMA 绑核对多路服务器很重要。
内存调优往往是被忽视的一环,很多人直接加内存完事。但优化 swap 和 cache 策略,有时能省下一台服务器。
你遇到过内存调优的奇葩问题吗?评论区分享一下,咱们一起分析。
更多推荐

所有评论(0)