Linux 内存:怎么看、底层机制与 OOM
都知道
free -h查看内存,但是真的看懂输出了吗?虚拟内存是什么?OOM 是什么? 本节讲的是怎么看内存 ,以及系统内存的底层原理与机制。
一、内存怎么看
bash
free -h
text
total used free shared buff/cache available
Mem: 15Gi 6.2Gi 1.1Gi 412Mi 8.4Gi 8.9Gi
Swap: 4.0Gi 0B 4.0Gi
这么多字段,哪个字段才能看内存?
free 字段显示只有 1.1Gi,是不是只有 1Gi 了?不是! 还是说要看 used 字段?也不是! 那 buff/cache 占比这么大,是不是说明内存不足了?这是缓存 (Linux 拿空闲内存缓存文件数据加速),应用需要时会自动回收。
其实真正要看的是 available 字段,真正缺内存的信号是 si/so ------ 如果 vmstat 里 si/so 持续不为 0,才说明内存真的紧张。至于 Swap 是什么,后续会讲。
free 各字段的含义:
| 字段 | 含义 |
|---|---|
total |
物理内存总量 |
used |
已用(= total − free − buff/cache) |
free |
完全没用的空闲(很小是正常的) |
shared |
tmpfs / 共享内存占用 |
buff/cache |
块设备缓冲 + 文件页缓存(可回收) |
available |
应用真正还能拿到的内存(看这个) |
1. 能不能手动释放 cache?
能但生产慎用 :echo 3 > /proc/sys/vm/drop_caches。它是缓存,不是泄漏;释放后系统还要重新读盘,性能反而变差。
2. 内存不够会有什么现象
- 系统变慢(频繁换页到 swap,
wa变高) - 进程被 OOM Killer 杀掉 →
dmesg | grep -i oom能查到 - 容器里的进程被杀 → 状态显示
OOMKilled - 新进程 fork 失败(
Cannot allocate memory) - 日志里出现
"Out of memory: Killed process ..."
3. 用 top 怎么排查进程的内存
这个专门写一节怎么运用
top指令的,等待更新
二、虚拟内存与缺页中断(机制层)
涉及的概念
| 概念 | 一句话 |
|---|---|
| 虚拟内存 | 进程看到的地址空间;提供隔离、连续地址、可换出到 swap、可共享 |
| 物理内存 | 真正装在机器上的内存条 |
| 页(page) | 内存管理的最小单位,通常 4KB |
| 页表 / TLB | 虚拟地址 → 物理地址的映射表 / 它的高速缓存 |
| 匿名页 | 没有对应文件的页(堆、栈、malloc 出来的内存)→ 回收时只能写 swap |
| 文件页 | 对应磁盘文件的页(程序、数据、page cache)→ 可直接丢弃再读回 |
1. 为什么需要虚拟内存
① 隔离 :进程 A 碰不到进程 B 的内存; ② 连续假象 :不必要求物理内存连续,解决碎片; ③ 超量使用:可以换出到 swap、可以按需加载。
2. 什么是缺页中断
- 进程访问一个虚拟地址
- CPU 查页表:发现这一页"不在物理内存"(页表项无效)
- 触发缺页异常(page fault),陷入内核
- 内核判断属于哪种情况:
- 从来没分配过 → 分配一个物理页(可能填零)
- 页在 page cache / 已在内存但没建立映射 → 建立映射
- 页被换出到 swap 或存在文件里 → 发起磁盘 IO 读回来
- 更新页表(并刷新 TLB)
- 返回用户态,重新执行刚才那条指令 ------ 这次就成功了
根据页是否在物理内存里,分成了两种缺页:
| 类型 | 定义 | 代价 | 典型场景 |
|---|---|---|---|
| minor(次要)缺页 | 页其实已经在物理内存里,只是还没建立映射 | 小(不涉及磁盘) | 首次访问 malloc 的堆、mmap 建立映射、写时复制 COW |
| major(主要)缺页 | 页不在内存,必须读磁盘(swap 或文件) | 大(一次磁盘 IO) | 进程被换出后又被唤醒、首次读大文件 |
性能经验 :major fault 持续很高 = 在疯狂读磁盘 / swap,系统会明显变卡 (这时候 si/so、wa 一般也高)。
3. 内存映射(mmap)是什么?有什么用?
mmap = 把一个文件(或一块匿名内存)映射到进程的虚拟地址空间 。 之后进程"读写内存"就等于"读写文件",不用 read/write 系统调用搬运 ------ Linux 的一切皆文件。
| 类型 | 说明 | 典型用途 |
|---|---|---|
| 文件映射 | 映射一个文件,页来自 page cache | 加载动态库(libc.so)、大文件随机读写 |
| 匿名映射 | 不关联文件,只能放内存 / swap | malloc 的大块内存、共享内存 |
| 私有(MAP_PRIVATE) | 写时复制(COW),改动不回写文件 | 加载程序段 |
| 共享(MAP_SHARED) | 多个进程看到同一份改动 | 进程间共享内存(IPC) |
它的好处:
- 快:少一次用户态 ↔ 内核态的数据拷贝(对大文件尤其明显)
- 共享:多个进程映射同一个文件 / 同一段匿名内存,天然共享
- 按需加载:只有真正访问到的页才读进来(配合缺页中断)
联系前面 :free 里的 buff/cache 很大一部分就是这些文件映射带来的 page cache ------ 它是缓存,不是内存不够。
三、swap 与 OOM
1. 什么是 swap?什么情况会用到 swap
text
swap = 磁盘/SSD 上划出来的一块空间,作为内存的后备。
内存紧张时,内核会先回收"文件页"(直接丢掉,需要时再读回来),
再回收不动就只好把"匿名页"写到 swap(换出),等进程再用到时再读回来(major fault)
bash
free -h ; swapon --show ; cat /proc/swaps # 查看 swap
vmstat 1 5 # 看 si/so
sysctl vm.swappiness # 看当前值
什么情况下"会用 swap" :① 内存压力大(
available低于水位);②swappiness偏高;③ 应用有大量"长时间不访问的匿名页";④ 内存限制(容器 / cgroup)被触发。 注意 :swap 用起来不是"故障",但 swap 频繁读写就是性能灾难(机械盘随机 IO 比内存慢几万倍)。很多数据库 / 中间件要求关掉 swap 或调到很低。
生产环境要不要开 swap?
普通服务器建议留一点(内存的 1
2 倍或按需,比如 48G)当缓冲 ,防止一次内存尖峰直接触发 OOM; 但数据库 / 中间件(MySQL、Redis)和 K8s 节点 一般要求关掉或把vm.swappiness调到 1~10,避免换页导致延迟抖动。
2. 什么是 OOM?什么导致了 OOM?
text
物理内存 + swap 都回收不出空间,新的内存申请无法满足
→ 内核的 OOM Killer 介入,按 oom_score 挑一个进程杀掉,回收它的内存
→ 目的是保住系统本身不崩
| 类别 | 具体原因 |
|---|---|
| 应用层 | ① 内存泄漏 (只申请不释放) ② 并发/流量激增,瞬时申请太多 ③ 一次性加载大文件、大查询结果集 ④ JVM/进程堆参数设得比物理内存还大(-Xmx 超配) |
| 内核参数 | ⑤ overcommit (允许超量分配,vm.overcommit_memory=1 时更容易 OOM) ⑥ swappiness / 水位设置不当,缓存回收不及时 |
| 资源规划 | ⑦ swap 太小或没开,没有缓冲 ⑧ 多个服务挤在一台机器上,总量超过物理内存 ⑨ 容器 / cgroup 内存 limit 设太小(容器里报 OOMKilled) |
| 不可回收 | ⑩ 内核页、共享内存段、锁定内存(mlock)太多,page cache 回收不动 |
OOM Killer 怎么挑人?
等待更新
出了 OOM 怎么查?
等待更新
四、内存泄漏排查
等待更新