Linux 内存使用完全指南:从 free 命令到 OOM Killer 的深入解析

为什么 Linux 内存管理值得你花时间理解?

如果你曾经在 Linux 服务器上运行过 free -h,看到这样的输出:

复制代码
              total        used        free      shared  buff/cache   available
Mem:           7.6G        5.2G        200M        120M        2.2G        6.8G
Swap:          4.0Gi          0B       4.0Gi

第一反应可能是------"天哪,用了 5.2G,只剩 200M 空闲了?是不是该加内存了?"

停。

这正是本文想要解决的第一个(也是最大的)误解。在 Linux 世界中,"已用"不意味着"已满","空闲"也不代表"可用"。5.2G 的 used 中,有 2.2G 是 buff/cache------而这些 cache,在应用程序真正需要时,是可以被内核回收的。

理解 Linux 内存管理,不仅能帮你避免不必要的硬件采购 ,还能在真正出现内存泄漏或 OOM(Out-Of-Memory)问题时,快速定位根因


free 命令全面解读

基础输出结构

运行 free -k(以 KB 为单位输出):

复制代码
              total        used        free      shared  buff/cache   available
Mem:        8009416     5463124      204892      123456     2341400     7102344
Swap:       2097148           0     2097148

每一列的含义:

含义 说明
total 总物理内存 从硬件检测到的 RAM 总量
used 已使用内存 total - free - buff/cache,注意这并不是"不可回收"的内存
free 完全未使用的内存 没有被任何进程或内核使用的"裸空闲"内存
shared 共享内存 主要用于 tmpfs、共享内存段(如 IPC)、Docker 容器等
buff/cache 缓冲区 + 缓存 内核用这部分内存来加速磁盘 I/O 和文件系统访问
available 可用内存 最关键的一列------新应用程序在不触发 swap 的情况下可以使用的内存量

核心要点:为什么 available 比 free 重要

free(空闲内存)告诉你的是完全没有任何用途 的内存数量。但 Linux 的设计哲学是:空闲内存就是浪费的内存

Linux 内核会积极地将空闲内存用于:

  • Page Cache(页面缓存):缓存磁盘文件内容,加速文件读写
  • Buffer Cache(缓冲区缓存):缓存块设备元数据
  • Slab 缓存:缓存内核对象(如 inode、dentry)

这些 cache 在应用程序需要更多内存时,可以被立即回收。这就是为什么 available 列被引入的初衷------它估算的是实际可用的内存量。

在较新的内核(3.14+)中,available 的计算考虑了:

  • MemFree
  • 可回收的 Page Cache
  • SReclaimable(可回收的 Slab 对象)
  • 文件备份页面的比例

buff/cache 深度解析

Page Cache(页面缓存)

当你在 Linux 上读取一个文件时,内核会将文件内容缓存到内存中。下次再次读取同一文件时,直接从内存返回------速度提升几个数量级

bash 复制代码
# 查看当前 page cache 大小
cat /proc/meminfo | grep -E "^Cached|^SwapCached"

Page Cache 是 Linux 内存使用中占比最大的部分。一个长期运行的服务器,大量内存被 Page Cache 占据是完全正常的。

Buffer Cache(缓冲区缓存)

Buffer Cache 缓存的是块设备的元数据,比如文件系统的超级块、inode 表等。在较新的内核中,Buffer Cache 和 Page Cache 已经部分合并,但在 /proc/meminfo 中仍然分开显示。

Slab 缓存

Slab 是内核用来管理小对象(如 inode、dentry、socket 缓冲等)的内存分配机制。

bash 复制代码
# 查看 slab 使用情况
slabtop -o

# 查看 slab 在总内存中的占比
cat /proc/meminfo | grep -E "^Slab|^SReclaimable|^SUnreclaim"

其中:

  • SReclaimable:可回收的 Slab 对象(如 dentry 缓存)
  • SUnreclaim:不可回收的 Slab 对象

手动清理缓存(调试用)

bash 复制代码
# 清理 page cache(仅 pagecache)
sync && echo 1 > /proc/sys/vm/drop_caches

# 清理 dentries 和 inodes
sync && echo 2 > /proc/sys/vm/drop_caches

# 清理 pagecache、dentries 和 inodes
sync && echo 3 > /proc/sys/vm/drop_caches

⚠️ 警告drop_caches 仅供测试和调试使用。在生产环境中,不要依赖它来"释放内存"。内核会自动管理缓存回收,手动清理反而会因为丢失缓存而导致暂时性的性能下降。


/proc/meminfo:内存信息的终极来源

free 命令的所有数据都来自 /proc/meminfo。直接查看它,可以获得更细粒度的信息:

bash 复制代码
cat /proc/meminfo

关键字段及其关系:

复制代码
MemTotal:        8009416 kB    # 总物理内存
MemFree:          204892 kB    # 完全空闲的内存(等同于 free 命令的 free)
MemAvailable:    7102344 kB    # 实际可用内存(关键指标)

Buffers:          307200 kB    # 块设备缓冲区
Cached:          1943200 kB    # Page Cache + tmpfs
SwapCached:            0 kB    # 已换出但仍在 swap 中的页

Active:          4200000 kB    # 最近被访问过的内存页
Inactive:        2800000 kB    # 较久未被访问的内存页
Active(anon):    2800000 kB    # 活跃的匿名页(进程堆栈等)
Inactive(anon):  1200000 kB    # 非活跃匿名页
Active(file):    1400000 kB    # 活跃的文件备份页
Inactive(file):  1600000 kB    # 非活跃文件备份页

Unevictable:       20000 kB    # 不可被换出的页面(如 mlock)
Mlocked:           20000 kB    # 通过 mlock() 锁定的页面

SwapTotal:       2097148 kB    # 交换分区总大小
SwapFree:        2097148 kB    # 交换分区可用大小

Dirty:              1200 kB    # 等待写入磁盘的脏页
Writeback:             0 kB    # 正在写回磁盘的页面

Slab:             450000 kB    # 内核 Slab 对象总大小
SReclaimable:     350000 kB    # 可回收的 Slab(如 dentry 缓存)
SUnreclaim:       100000 kB    # 不可回收的 Slab

PageTables:        32000 kB    # 页表占用的内存(进程越多越大)
NFS_Unstable:          0 kB    # NFS 未确认的页面
Bounce:                0 kB    # 用于 bounce buffer 的内存

CommitLimit:     6100000 kB    # 基于 overcommit 策略的提交限制
Committed_AS:    5500000 kB    # 已commit的虚拟内存总量

MemAvailable 是"估算值"

MemAvailable 不是直接测量的值,而是内核基于当前状态估算出来的。它的计算方式大致是:

复制代码
MemAvailable ≈ MemFree + (PageCache 可回收部分) + (SReclaimable 可回收部分) - 低水位预留

这意味着即使在内存压力下,这个值也会动态变化。它是监控中最可靠的单一指标。

Active 与 Inactive 的意义

内存页分为活跃(Active)和非活跃(Inactive)两种状态:

  • Active:最近被访问过的页面,内核尽量保留
  • Inactive:已经有一段时间未被访问,是内核回收的首选

当系统可用内存减少时,内核会首先扫描并回收 Inactive 列表中的页面。


进程级别的内存:VIRT、RES、SHR

使用 topps 查看进程内存时,你会看到三个关键指标:

VIRT(Virtual Memory Size)------ 虚拟内存

进程申请的虚拟地址空间大小。包括:

  • 进程的代码段、数据段、堆(heap)、栈(stack)
  • 映射的文件(如共享库)
  • 已申请但尚未实际分配的页面
bash 复制代码
# 示例:一个 Python 进程
VIRT = 2.5G    # 看起来很大,但一部分可能并未实际分配物理内存

VIRT 大不等于内存泄漏。很多进程预分配了大量虚拟地址空间(如 JVM),但实际使用的物理内存可能只有一小部分。

RES(Resident Size)------ 常驻内存

实际驻留在物理内存中的页面大小 。这是衡量进程真实内存占用的最可靠指标

bash 复制代码
# 按 RES 排序查看进程
top -o %MEM

# 或者
ps aux --sort=-%mem | head -10

RES 包括:

  • 进程独占的匿名页面(堆、栈)
  • 共享库中实际加载到内存的部分
  • 映射文件中被缓存到内存的部分

注意:RES 包含共享内存中归属于该进程的部分,所以多个进程的 RES 之和可能超过总内存。

SHR(Shared Memory)------ 共享内存

进程实际使用的共享内存大小。典型场景:

bash 复制代码
# 查看共享内存使用情况
ipcs -m

实用技巧top 中按 f 键然后选择 nTH 可以显示进程的线程数 ;按 f 选择 MEM 可以看到内存占比。


核心监控工具实战

vmstat ------ 虚拟内存统计

bash 复制代码
# 每 2 秒输出一次,显示单位为 K
vmstat 2

# 清晰的宽格式输出
vmstat -w 2

关键列解读:

含义 正常值
r 运行队列中的进程数 < CPU 核数 x 2
b 不可中断休眠进程数 接近 0
swpd 已使用的 swap 尽量接近 0
free 空闲内存 不重要,看 available
buff 缓冲区 随负载变化
cache 页面缓存 越大越好
si 从 swap 换入 长期 > 0 需要关注
so 换出到 swap 长期 > 0 需要关注
bi 块设备读入 随 I/O 负载变化
bo 块设备写出 随 I/O 负载变化
wa CPU 等待 I/O 时间 正常 < 10%

黄金指标 :如果 siso 长期大于 0,说明系统真正内存不足,正在频繁换页。

sar -r ------ 历史内存趋势

bash 复制代码
# 查看今天的每小时内存使用趋势
sar -r

# 每 2 秒收集一次,共 5 次
sar -r 2 5

# 查看特定日期的历史数据
sar -r -f /var/log/sysstat/sa23

sar -r 输出中的关键指标:

  • kbmemfree / kbmemused:空闲/已用内存
  • kbbuffers / kbcached:缓冲区/缓存
  • kbcommit / %commit:已提交的内存比例

tophtop

bash 复制代码
# top 交互模式中的快捷键
top
# 按 M:按内存使用排序
# 按 P:按 CPU 使用排序  
# 按 e:切换内存显示单位
# 按 f:选择要显示的列

htop 提供了更友好的界面,支持颜色编码和鼠标操作。

ps 快速排查

bash 复制代码
# 按内存使用排序,查看 Top 10 进程
ps aux --sort=-%mem | head -10

# 查看特定进程的内存
ps -p <PID> -o pid,ppid,rss,vsz,%mem,cmd

Swap 深度理解

什么是 Swap?

Swap 是磁盘上用于扩展内存的空间。当物理内存不足时,内核会将不活跃的内存页换出(swap out)到磁盘,腾出物理内存给活跃进程。

Swap 是好是坏?

这是 Linux 社区最常见的争论之一。正确的理解是:

Swap 不是"紧急逃生通道"------它是内存层次结构中的正常一层。

  • 好的 Swap 使用:将长时间不活跃的进程页面换出,让缓存有更多空间加速文件访问
  • 坏的 Swap 使用:因内存不足,导致活跃进程的页面也被频繁换入换出(thrashing)

关键参数:swappiness

bash 复制代码
# 查看当前 swappiness 值
cat /proc/sys/vm/swappiness

# 临时修改(重启后失效)
sysctl vm.swappiness=10

# 永久修改
echo "vm.swappiness=10" >> /etc/sysctl.conf

swappiness 范围 0~100(默认 60):

  • 值越低:越倾向于使用物理内存,只在内存极度紧张时才 swap
  • 值越高:越激进地使用 swap(但并不意味着性能更好)

对于现代服务器(特别是 SSD),推荐值:

  • 数据库服务器:1~10
  • Web 服务器:10~30
  • 桌面系统:60(默认)

Swap 与 OOM 的关系

很多人认为"加 swap 可以防止 OOM"。不完全正确

Swap 只是延缓了 OOM 的发生。如果内存泄漏的速度超过了 swap 空间 + 物理内存的总和,OOM Killer 仍然会被触发。

更准确的说法是:充足的 swap 可以给运维人员争取时间来诊断问题。


OOM Killer:最后的防线

何时触发 OOM Killer?

当系统物理内存 + swap 均已耗尽,且内核的"内存超额分配"(overcommit)策略导致无法满足新的内存申请时,OOM Killer 会选择一个进程杀死。

查看 OOM Killer 日志

bash 复制代码
# 查看 OOM Killer 活动记录
dmesg | grep -i "killed process"

# 或者查看内核日志
grep -i "oom" /var/log/messages
# 或者
grep -i "oom" /var/log/kern.log

典型 OOM 日志:

复制代码
[123456.789] oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=0
[123456.789] [ pid ]   uid  tgid total_vm      rss nr_ptes nr_pmds swapents oom_score_adj name
[123456.789] [ 1234]     0  1234  1234567    98765    123      4     5678             0 java
[123456.789] Out of memory: Killed process 1234 (java) total-vm:1234567kB, anon-rss:98765kB, file-rss:0kB

OOM Killer 选择谁?

内核使用 oom_score 来评分------分数越高的进程越容易被杀死。

bash 复制代码
# 查看进程的 oom_score
cat /proc/<PID>/oom_score

# 调整进程的 OOM 优先级(-1000 到 1000)
echo -500 > /proc/<PID>/oom_score_adj

影响 oom_score 的因素:

oom_score 的计算几乎纯粹取决于该进程占用的内存比例(实际物理内存 RSS + 换出的 Swap 空间),再叠加上 oom_score_adj 的修正值

如何防止 OOM?

  1. 配置合理的 swappiness:非数据库服务器可以保留默认值
  2. 设置进程内存限制(cgroups/systemd):
bash 复制代码
# 通过 systemd 限制服务内存
systemctl set-property <service-name> MemoryMax=1G
  1. 关闭内存超额分配(谨慎!):
bash 复制代码
# 0 = 启发式 overcommit(默认)
# 1 = 总是 overcommit
# 2 = 禁止 overcommit(拒绝超过 CommitLimit 的分配)
sysctl vm.overcommit_memory=2
sysctl vm.overcommit_ratio=50  # 允许 overcommit 的比例
  1. 使用限制内存的容器运行应用(如 Docker memory limits)
  2. 监控 available 内存,设置合理告警阈值

本文参考自 Heroix Blog - Linux Memory Use,这是一篇经典的 Linux 内存管理入门文章。其核心观点------"Because Linux uses free memory for buffers and caches, monitoring for memory problems is more complex than simply looking at the free memory value on the system"------至今仍然是每一位 Linux 运维人员需要铭记的原则。


附录:速查命令表

命令 用途 示例
free -h 查看内存概览 free -h
cat /proc/meminfo 查看详细内存指标 grep Mem /proc/meminfo
vmstat -w 2 实时虚拟内存统计 vmstat -w 2 10
top -o %MEM 按内存排序查看进程 `top -b -n 1 -o %MEM
ps aux --sort=-%mem 进程内存排序 `ps aux --sort=-%mem
sar -r 历史内存趋势 sar -r -f /var/log/sysstat/sa23
slabtop -s c 查看 slab 缓存 `slabtop -o
`dmesg grep -i oom` OOM 日志
ipcs -m 共享内存段 ipcs -m -u
sysctl vm.swappiness 查看/设置 swappiness sysctl -w vm.swappiness=10

希望这篇文章能帮助你更好地理解 Linux 内存管理。如果你有任何问题或发现需要补充的内容,欢迎交流讨论!