「服务器内存快满了!」------这是运维群里出现频率最高的一句话,也是误报率最高的一句话。因为很多人只看 free 的 free 列,没搞清 buff/cache 和 available 的区别,把健康的机器当成内存泄漏来排障。这一篇把 free、vmstat、buff/cache、swap 讲清楚,看完你就能准确判断:到底是真的内存不够,还是 Linux 在「合理利用」。
一、free:看一眼内存全貌
语法格式
free [选项]
常用参数
| 参数 | 说明 |
|---|---|
-h |
人性化单位(GiB/MiB),最常用 |
-m / -g |
以 MB / GB 为单位 |
-s 秒 |
每隔 N 秒刷新一次 |
-t |
多一行显示总计 |
示例与预期输出
free -h
预期输出:
total used free shared buff/cache available
Mem: 7.6Gi 3.1Gi 512Mi 20Mi 4.0Gi 4.1Gi
Swap: 2.0Gi 128Mi 1.9Gi
逐列读懂:
| 列 | 含义 |
|---|---|
total |
物理内存总量 |
used |
已用(= total - free - buff/cache) |
free |
完全空闲的内存,这个数字小很正常 |
shared |
共享内存(tmpfs、shm)占用 |
buff/cache |
内核缓冲区 + 页缓存,可回收 |
available |
估算的真正可用内存,最重要 |
结论先行:判断内存是否紧张,看 available,不看 free。
二、buff/cache:不是「被吃掉」,是「被借用」
Linux 的设计哲学是:空闲的内存是浪费的内存 。所以内核会把所有暂时不用的内存拿来缓存磁盘数据(page cache)和块设备元数据(buffer cache),这就是 buff/cache。
关键在于:这部分内存随时可以被回收。当应用需要内存时,内核优先把 cache 里的页面释放出来给它。所以:
free列很小 → 正常,内存被有效利用了buff/cache很大 → 好现象,说明读磁盘命中缓存多了available很小 → 这才是真信号
available 是内核综合「free + 可回收的 cache」给出的估算值。它大,说明真要起来还能挤出内存;它小,说明可回收的都回收得差不多了,再来应用就得动 swap 或触发 OOM。
想看 cache 的构成
grep -E "Buffers|Cached|MemAvailable|Dirty" /proc/meminfo
Buffers: 156384 kB
Cached: 3856212 kB
Dirty: 12048 kB
MemAvailable: 4218364 kB
Buffers:块设备元数据缓存Cached:文件内容的页缓存Dirty:被改过但还没写回磁盘的页,这个数持续增长要警惕MemAvailable:available的原始值
三、vmstat:一行看全 VM / IO / CPU
free 只看内存,vmstat 一次给你内存、分页、IO、CPU 全景,是排查性能问题的一把好手。
语法格式
vmstat [间隔秒] [次数]
常用参数
| 参数 | 说明 |
|---|---|
1 5 |
每秒采样一次,共 5 次 |
-s |
显示内存统计汇总 |
-d |
显示磁盘统计 |
-w |
宽格式输出,防止列被挤在一起 |
示例与预期输出
vmstat 1 5
预期输出:
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 131072 524288 156384 3856212 0 0 12 45 250 480 6 2 91 1 0
0 0 131072 520000 156400 3857000 0 0 0 60 240 460 5 2 93 0 0
各列含义(重点标出):
| 列 | 含义 | 判断 |
|---|---|---|
r |
运行队列长度(等 CPU 的进程数) | 持续 > 核心数,CPU 紧张 |
b |
处于不可中断睡眠的进程数 | 持续 > 0,可能 IO 卡住 |
si / so |
换入/换出 swap 的量(KB/s) | 持续非 0 = 内存不足 |
bi / bo |
块设备读/写(KB/s) | 看磁盘活跃度 |
in / cs |
中断数 / 上下文切换数 | cs 异常高看中断或锁 |
us/sy/id/wa |
用户/系统/空闲/IO 等待占比 | wa 高 = 磁盘瓶颈 |
⚠️ 最关键的判断:si/so
si 和 so 长期为 0 ,说明内存够用,swap 没被真正使用。
si/so 持续大于 0,说明物理内存不够了,系统在「颠簸」(thrashing)------反复把页换出换入硬盘,性能断崖式下跌。
看到 swap 里「有数据」不等于「在用 swap」。判断 swap 是否被高频使用,看的正是 si/so,不是 free 里 swap 的 used。
四、swap:不是「内存不够」的罪证
swap 是磁盘上的一块交换空间。它的作用有两个:
- 给内核留缓冲:把长期不访问的匿名页换出去,腾出物理内存。
- 应对突发峰值:短时内存冲高,先换一些出去扛过去。
合理的 swap 使用是健康的。真正要警惕的是高频换页 (si/so 持续非 0),因为磁盘比内存慢好几个数量级,一旦高频换页,系统基本就废了。
swappiness:调节换页倾向
cat /proc/sys/vm/swappiness
默认通常 60。含义是「内核有多倾向于把匿名页换出」:
60:比较积极,普通桌面/通用服务器10:低,数据库服务器常用(数据库自己有缓存,不想要内核乱换)0:除非内存真的要用光,否则不换
临时调整:
sudo sysctl vm.swappiness=10
永久生效写入 /etc/sysctl.conf:
vm.swappiness = 10
⚠️ 要不要关掉 swap
- 不要直接
swapoff -a:真到内存峰值时,没有 swap 缓冲会直接触发 OOM 杀进程。 - 数据库主机可以降低 swappiness(如 1~10),但保留一定 swap 作保险。
- Kubernetes 节点 traditionally 禁用 swap,因为调度器假设内存是独占的(新版支持 swap 但需配置)。
五、内存告急的完整排查路径
真遇到内存问题,按这个顺序走:
# 1. 看总量和可用量
free -h
# 2. 看是否高频换页
vmstat 1 5
# 3. 找出吃内存最多的进程
ps aux --sort=-%mem | head -10
# 4. 看内核 slab 缓存是否异常(内核对象泄漏)
sudo slabtop -o | head -15
# 5. 看共享内存段
ipcs -m
# 6. 看是否有大量脏页未回写
grep -E "Dirty|Writeback" /proc/meminfo
⚠️ 常见错误
- 看到 free 小就重启服务 。
free小 +available大是健康的,重启服务反而清空了 cache,让后续读盘变慢。 - 把 buff/cache 当泄漏 。它是可回收的,不是应用吃的。真要看应用内存,用
ps的 RSS 或smem看 PSS。 - 误判 swap 使用 。
free里 swap used > 0 不代表正在颠簸,必须结合vmstat的si/so看。 - 只盯单一进程忘了内核 。slab 缓存(
Slab)异常增长也是内存消失的常见原因,slabtop要会用。 - 没配 swap 却说「内存够」 。没 swap 时,
available逼近 0 会直接触发 OOM,没有缓冲余地。
六、原理探究:页缓存、脏页与回写
理解内存管理,绕不开 页(page)。Linux 以 4KB 为基本单位管理内存(大页场景为 2MB 或 1GB),所有内存操作都围绕页展开。
page cache 是怎么来的
你 cat bigfile 或程序 read() 文件时,数据不是直接从磁盘进应用缓冲区,而是:
磁盘 → 页缓存(page cache) → 应用缓冲区
第一次读文件,数据被读进 page cache,同时拷给应用。再读同一文件,直接从 cache 返回,不碰磁盘 。这就是「第二次读文件飞快」的原因,也是 buff/cache 不断增长的来源。
脏页与回写
应用 write() 文件时,数据先写进 page cache,标记为 dirty(脏页) ,然后立即返回------写操作并没有立刻落盘 。内核的**回写线程(kworker/flush)**会在稍后把脏页批量刷到磁盘。
这带来两个后果:
- 性能好:写入合并、批量落盘,减少磁盘次数。
- 有两个坑 :
- 断电丢数据 :未回写的脏页断电就没了。所以重要数据要
fsync()。 - 脏页堆积 :写入速度远超磁盘回写速度,脏页越积越多,
/proc/meminfo里Dirty暴涨,内核会开始强制同步回写,导致写操作突然变慢(这就是「写风暴」)。
- 断电丢数据 :未回写的脏页断电就没了。所以重要数据要
关键内核参数
sysctl vm.dirty_ratio vm.dirty_background_ratio
| 参数 | 默认 | 含义 |
|---|---|---|
vm.dirty_background_ratio |
10 | 脏页占比超过它,后台开始回写 |
vm.dirty_ratio |
20 | 超过它,进程写操作被阻塞,强制回写 |
vm.dirty_expire_centisecs |
3000 | 脏页超过 30 秒强制回写 |
理解这两个阈值,就能解释「为什么大量写文件时程序会突然卡一下」------正好撞上了强制回写。
七、OOM Killer:最后的手段
当内存和 swap 都用尽,内核会启动 OOM Killer ,挑一个进程杀掉。选谁下手看 oom_score:
cat /proc/<PID>/oom_score
cat /proc/<PID>/oom_score_adj
oom_score 越高越容易被杀。你可以手动调整倾向:
echo -1000 > /proc/<PID>/oom_score_adj # 尽量别杀它(需 root)
-1000 表示「永不杀」,1000 表示「优先杀」。关键服务可以设低,防止被误杀。
看 OOM 日志
dmesg -T | grep -i "out of memory"
journalctl -k | grep -i oom
日志里会写清楚「killed process XXX (total-vm...)」,这就是定位内存事故的现场记录。
八、几张表总结
free 列速查:
| 列 | 该不该担心 |
|---|---|
free 小 |
不担心 |
buff/cache 大 |
不担心 |
available 小 |
担心 |
| swap used 大但 si/so 为 0 | 不担心 |
| swap used 大且 si/so 持续非 0 | 担心 |
内存指标一句话记忆:
- 看内存够不够 →
available - 看有没有颠簸 →
vmstat的si/so - 看谁吃的 →
ps的 RSS /smem的 PSS - 看内核吃的 →
slabtop、/proc/meminfo - 看写压力 →
/proc/meminfo的Dirty/Writeback
内存是 Linux 最容易被误解的子系统。记住核心一句:Linux 会把空闲内存用到极致,free 少不代表内存不够,available 才是你要盯的数字。