﻿# free —— 查看内存与 swap 使用情况

## 这个工具是做什么的

一眼看整机内存够不够、有没有在用 swap，是判断"内存瓶颈"的第一步。核心是**理解 Linux 的缓存机制**——空闲内存被拿去做缓存是好事，不是内存不足。

## 可以回答什么问题

| 问题 | 怎么用 |
|------|--------|
| 物理内存够不够？ | `free -h` 看 `available` 列，不是 `free` 列 |
| 有没有在用 swap？swap 在持续增长吗？ | `free -h` 的 Swap 行 `used` 列 |
| 缓存占了多少内存？还可以回收多少？ | `buff/cache` 列（可回收的缓存） |
| 内存趋势是增加还是减少？ | `free -h -s 2` 持续观察 |

## 数据来源

- **来源文件**：`/proc/meminfo`（`MemTotal`/`MemFree`/`MemAvailable`/`Buffers`/`Cached`/`SwapTotal`/`SwapFree` 等）。
- **采集方式**：不做差、无需间隔——每次直接读一遍 `/proc/meminfo` 当前快照并换算成表格。
- **由此决定的特性**：`available` 这一列**直接取自内核算好的 `MemAvailable`**（内核综合了可回收缓存的估算），比自己拿 `free + cached` 手算更准——判断内存紧不紧就认这一列（详见正文"常见误区"）。

```bash
free -h                  # 人类可读单位（自动 Ki/Mi/Gi，最常用）
free -m                  # 固定以 MB 显示
free -g                  # 以 GB 显示（大内存机）
free -h -s 2             # -s 每 2 秒刷新一次（持续观察）
free -h -c 5 -s 1        # -c 刷新 5 次后退出
free -h -t               # -t 额外加一行 Total（内存+swap 合计）
free -w -h               # -w 把 buffers 和 cache 拆成两列显示
```

## 一、输出与字段

```bash
              total   used   free   shared  buff/cache   available
Mem:           15Gi   4.0Gi  1.0Gi   200Mi      10Gi        10Gi
Swap:          2.0Gi   0B     2.0Gi
```

| 字段 | 含义 | 说明 |
|------|------|------|
| `total` | 总物理内存 | 略小于物理条容量（内核/固件占用）|
| `used` | 已用 | = total − free − buff/cache（真正被进程占的）|
| `free` | 完全空闲、未被任何用途占用 | **数值小是正常的**，见下 |
| `shared` | 共享内存 | tmpfs、`/dev/shm`、进程间共享段 |
| `buff/cache` | 缓冲 + 页缓存（合并显示）| **可回收**：内核拿空闲内存缓存磁盘内容加速 IO，进程要用时立即让出 |
| `available` | **真正可用内存** | 估算值 = free + 可回收的 cache。**判断内存紧不紧只看这个** |

Swap 行：`used` 非 0 且持续增长，说明物理内存不够，内核把冷页换到磁盘。

### `buff/cache` 拆分：用 `free -w` 看清两个独立含义

默认 `free -h` 把 `buffers` 和 `cached` 合成一列（`buff/cache`），但它们是两种不同用途的缓存：

```bash
free -h -w           # -w: wide, 把 buffers 和 cache 拆成两列
```

输出示例：

```bash
              total   used   free   shared  buffers  cache  available
Mem:            15Gi  4.0Gi  1.0Gi   200Mi     180Mi  9.8Gi     10Gi
Swap:          2.0Gi     0B  2.0Gi
```

| 列 | 实质 | 产生场景 | 典型大小 |
|----|------|---------|---------|
| `buffers`（= vmstat 的 `buff`） | **块设备缓冲**：裸盘元数据、块设备层缓冲 | 裸块设备读写（`dd` 直写 `/dev/sda`）、ext4 日志、文件系统元数据 | 普通文件 IO 下几十~几百 MB；裸盘直写时可能 GB |
| `cache`（= vmstat 的 `cache`） | **页缓存**：文件内容、mmap 映射、文件 inode 缓存 | 读文件、运行程序加载 `.so`/二进制、`grep` 扫描大文件、文件 mmap | 视业务：空闲时把剩余内存几乎全部用作 page cache（可达总内存 50-80%） |

**两者的关系**：在现代 Linux（2.4+）上，`buffers` 是 page cache 的一个子集（主要针对裸块设备和元数据），两者都是可回收的，回收顺序和策略完全一致——所以默认 `buff/cache` 合并显示是合理的，**判断内存紧不紧还是看 `available`**。

> 💡 **vmstat 也有 `buff` 和 `cache` 两列**——含义和 `free -w` 的 `buffers`/`cache` 一致。所以 `free` 和 `vmstat` 的数据可对照：两边 `cache` 数字应近似相同（差几个 MB 属正常，因采样时点不同）。详见 [vmstat memory 四列解读](/tools/cpu/vmstat.md#memory-四列的实操解读看-swpd--free--buff--cache-四个数到底在说什么)。

## 二、最重要的认知：free 小 ≠ 内存不足

Linux 的哲学是"空闲内存就是浪费"，所以会主动把 RAM 用作 `buff/cache`。因此：

- **`free` 很小、`buff/cache` 很大 = 健康**：缓存随时可回收，`available` 才是可用量。
- **判断内存是否吃紧：只看 `available`**。它接近 0 才是真紧张。
- 想手动看回收效果（一般不需要，仅调试）：`sync; echo 1 > /proc/sys/vm/drop_caches` 会清页缓存，`free`/`available` 立刻变大——证明那部分本就是可回收的。

### free / buff / cache / swpd 的关系图

```plantuml
@startuml
skinparam rectangle {
  RoundCorner 15
}

rectangle "物理内存" as RAM {
  rectangle "真正空闲 (free)" as FREE
  rectangle "进程占用 (used)" as USED
  rectangle "buffers (块设备元数据)" as BUFF
  rectangle "cache (页缓存, 文件内容)" as CACHE
}

rectangle "Swap 空间" as SWAP {
  rectangle "已用 swap (swpd)" as SWPD
}

USED ..> SWAP : "so (内存吃紧时换出)"
SWAP ..> USED : "si (页面被访问时换入)"

note right of CACHE
  内核主动用空闲内存
  缓存文件内容, 加速读
  内存紧时直接 drop
end note

note right of FREE
  available = free + 可回收 cache
  判断内存紧不紧只看 available
end note

note bottom of SWPD
  swpd 涨 + si/so 非零
  = 内存真不足
end note

@enduml
```

**关键概念**：
- `free` 看着小是常态——空闲内存被内核拿去做 `cache`。
- `cache` 是可回收的缓存，进程要内存时内核立即让出，**不算真占用**。
- `available` 才是真正可用内存的估算值（≈ free + 可回收 cache）——**判断内存是否吃紧只认这一列**。
- `swpd` 是已换出的累计量，**涨 + si/so 非零**才是真问题；稳定不变是历史残留。

## 三、内存真紧张的信号（组合判断）

同时出现才下结论：

1. `free -h` 的 `available` 持续很低（如 <5% total）；
2. `Swap` 的 `used` 在**持续增长**；
3. `vmstat 1` 的 `si`/`so` **持续非 0**（正在换页）；
4. `dmesg | grep -i oom` 出现 OOM Killer 记录（已经杀进程了）。

→ 处理：找吃内存大户、加内存、限制进程内存、排查内存泄漏。

## 四、关键指标速查

| 指标 | 缩写/英文 | 正常值 | 异常信号 |
|------|----------|--------|---------|
| 真正可用内存 | `available` / MemAvailable | 充足（> 10% total） | < 5% total → 内存吃紧，随时 OOM |
| 空闲内存 | `free` / MemFree | — | 小 = 正常（被缓存利用），关键看 available |
| 缓存占用 | `buff/cache` | 视负载 | 过大 + available 低 → 可回收但被锁定 |
| Swap 使用量 | `Swap used` | 0 或稳定 | 持续增长 → 内存压力在累积 |
| 共享内存 | `shared` | 视业务 | 突然暴涨 → 大量 tmpfs 或进程间共享 |

## 五、常见排查方法与分析

### 5.1 内存不足确认

```bash
free -h → available 接近 0
  → free -h -s 2 → 看 available 是否持续下降
    → vmstat 1 → 看 si/so 是否非零（正在换页的铁证）
      → dmesg | grep -i oom → 是否已有 OOM Kill 记录
        → ps aux --sort=-%mem | head → 找吃内存大户
```

### 5.2 Swap 持续增长分析

```bash
free -h -s 2 → Swap used 持续增长
  → vmstat 1 → si/so 持续非零
    → pidstat -r 1 → 看 majflt/s 最高的进程
      → cat /proc/<PID>/status | grep VmSwap → 哪个进程在被换出
```

### 5.3 内存泄漏观察

```bash
free -h -s 5 → available 持续下降、used 持续上升
  → ps aux --sort=-%mem | head → 找 RSS 最大的进程
    → pidstat -r -p <PID> 5 → 持续观察该进程 RSS 趋势
      → 若 RSS 单调增长 → 内存泄漏
```

### 5.4 available 和 free 都小但系统正常

Linux 拿空闲内存做缓存是正常的。确认是否真紧张：

```bash
free -h → available 很大但 free 很小 → 健康，缓存可回收
free -h → available 也很小 → 紧张
  → 同时检查 vmstat 1 的 si/so → 非零 = 真的不足
```

## 六、交叉引用

- **内存全景**：[procfs](/tools/proc/procfs.md) §三（`/proc/meminfo` 是最详细的内存明细）
- **虚拟内存检查**：[vmstat](/tools/cpu/vmstat.md)（si/so 换页速率，验证内存是否真的不足）
- **进程级缺页**：[pidstat](/tools/cpu/pidstat.md) `-r`（majflt/s 看谁在触发主缺页）
- **Cgroup 限制**：[kernel-tuning](/tools/proc/kernel-tuning.md) §一（memory cgroup 限制影响 free 读数）
- **OOM 排查**：[core-dump](/crash/core-dump.md)（OOM 时抓 core）
- **Swap 调优**：[kernel-tuning](/tools/proc/kernel-tuning.md) §一（vm.swappiness 控制换页倾向）

## 七、关联命令

```bash
cat /proc/meminfo              # 最详细的内存明细（free 就是读它算出来的）
vmstat 1                       # 看 si/so 判断是否在 swap
ps aux --sort=-%mem | head     # 按内存占用排序，找 Top 进程
top    # 按 M 排序，看 RES（常驻物理内存，比 VIRT 真实）
pidstat -r -p <PID> 1          # 看某进程的 RSS 与缺页率 majflt/s
smem -tk                       # 更准的按进程内存（区分 USS/PSS/RSS，需另装）
dmesg -T | grep -i oom         # 有没有触发过 OOM Killer
```

> `VIRT`（虚拟内存）通常远大于真实占用（含未触碰的映射、mmap 文件），**看真实内存占用要看 `RES`/`RSS`**。多进程共享库时想避免重复计算，用 `smem` 的 PSS 更准。

