LinuxNotes

第 15 章:内存管理

zjc 于 2026-01-15 发布

这是《Linux 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Linux 内存管理包括虚拟内存、页表、缺页、文件页缓存、匿名页、swap、OOM 和 cgroup 限制。理解这些概念,才能解释“free 里 available 才是关键”“进程 RSS 为什么持续增长”“容器内存为什么会被杀”。

15.1 free 输出

free -h
free -m
cat /proc/meminfo

示例:

              total   used   free   buff/cache   available
Mem:           15G    8G     1G         6G         6G
Swap:           4G    1G     3G

关键指标:

指标 含义
free 完全未使用内存
buff/cache 内核页缓存和缓冲
available 估算可供新进程使用
swap 交换分区使用

Linux 会把空闲内存用作页缓存,所以 free 低不等于内存不足。判断压力应看 available、swap in/out、缺页、回收和 OOM 事件。

15.2 虚拟内存

Virtual Address Space
  |-- code
  |-- data
  |-- heap
  |-- shared libraries
  |-- stack
+-- mmapped files / anonymous memory

进程视角:

cat /proc/<pid>/status | grep -E 'Vm|Rss'
cat /proc/<pid>/smaps_rollup
pmap -x <pid>

指标:

指标 含义
VmSize / VSS 虚拟地址空间大小
VmRSS / RSS 常驻物理内存
PSS 按共享比例分摊后的内存
USS 进程独占内存
Swap 已换出内存

容量评估优先看 PSS,而不是只看 VSS。

15.3 页类型

页类型 说明 回收方式
File-backed page 文件页缓存 可丢弃或回写
Anonymous page 堆、栈等 需写 swap 或压缩
Locked page mlock 等 通常不回收
Slab 内核对象缓存 按对象回收

观察:

cat /proc/meminfo | grep -E 'Cached|Buffers|AnonPages|SReclaimable|SUnreclaim'
vmstat 1

15.4 缺页与交换

vmstat 1
cat /proc/vmstat | grep -E 'pgfault|pgmajfault|pswpin|pswpout'

关键指标:

指标 含义
bi / bo 块设备读写
si / so swap in / out
majflt major fault

判断:

现象 方向
si/so 持续大于 0 内存压力明显
majflt 高 文件页未命中
available 低 接近容量
OOM 日志 已发生牺牲

15.5 OOM Killer

查看:

dmesg -T | grep -Ei 'out of memory|oom|killed process'
journalctl -k --since today | grep -i oom

OOM 会根据内存压力和 oom_score 选择牺牲进程:

cat /proc/<pid>/oom_score
cat /proc/<pid>/oom_score_adj

调整:

echo -500 > /proc/<pid>/oom_score_adj

生产重点不是调整分数,而是:

  1. 控制内存上限;
  2. 修复泄漏;
  3. 设置水位告警;
  4. 保证关键服务有逃生容量;
  5. 通过编排重启策略恢复。

15.6 内存泄漏排查

进程级:

watch -n 5 'grep -E "VmRSS|VmSwap" /proc/<pid>/status'
pidstat -r -p <pid> 5
smem -rk | head -20

系统级:

ps -eo pid,user,%mem,rss,cmd --sort=-rss | head -20
slabtop
cat /proc/meminfo

应用侧:

  1. Java 使用 heap dump 和 GC 日志;
  2. Go 使用 pprof;
  3. C/C++ 使用 valgrind、ASan 或 core;
  4. Python 使用 tracemalloc。

RSS 持续增长不一定是泄漏,也可能是缓存增长。要结合配置上限和业务请求量判断。

15.7 cgroup 内存

查看:

cat /sys/fs/cgroup/<path>/memory.current
cat /sys/fs/cgroup/<path>/memory.max
cat /sys/fs/cgroup/<path>/memory.stat

关键项:

指标 含义
anon 匿名页
file 文件页
slab 内核 slab
pgfault 缺页
pgmajfault major fault
workingset_refault 工作集回取

容器内存达到 hard limit 时可能被杀。判断要看 cgroup 事件和编排系统状态:

kubectl describe pod <pod>
docker inspect <container> --format '{{.State.OOMKilled}}'

Java 容器应让 JVM 感知容器限制,并保留堆外内存和元空间余量。

15.8 透明大页

查看:

cat /sys/kernel/mm/transparent_hugepage/enabled
cat /sys/kernel/mm/transparent_hugepage/defrag

常见值:

always
madvise
never

数据库通常关注 THP 对延迟抖动的影响,具体推荐以数据库和发行版文档为准。修改前要记录默认值和回滚方式。

15.9 内存调优原则

先消除问题,再考虑参数:

1. 确认内存压力证据
2. 定位进程或内核内存
3. 区分缓存增长和泄漏
4. 修复应用配置和代码
5. 评估容量和限流
6. 调整缓存大小
7. 最后评估 swap 和内核参数

常用参数:

sysctl vm.swappiness
sysctl vm.overcommit_memory
sysctl vm.min_free_kbytes

swappiness 不是百分比开关,语义和效果依赖 workload。不要脱离压测直接照抄。

15.10 常见案例

案例一:available 低

free -h
vmstat 1
ps -eo pid,rss,cmd --sort=-rss | head
cat /proc/meminfo

处理:

  1. 定位大进程;
  2. 检查缓存上限;
  3. 重启或扩容前评估影响;
  4. 增加提前告警。

案例二:Java 容器 OOMKilled

kubectl describe pod <pod>
kubectl top pod <pod>

方向:

  1. -Xmx 过大;
  2. 堆外内存或线程栈增长;
  3. limit 太小;
  4. 内存泄漏;
  5. 请求量超容量。

案例三:机器有 swap 但延迟抖动

vmstat 1
grep -E 'pswpin|pswpout' /proc/vmstat

如果 si/so 持续,说明应用工作集超过物理内存。应扩容或降低内存消耗,而不是只调大 swap。

本章小结

Linux 会把空闲内存用作缓存,判断内存压力要看 available、swap、major fault、回收和 OOM。进程内存要区分 VSS、RSS 和 PSS;容器要区分应用使用、页缓存和 cgroup hard limit。内存治理重点是容量预算、泄漏定位和水位告警。

思考题

  1. freeavailable 有什么区别?
  2. RSS 高是否一定代表泄漏?
  3. cgroup memory.max 达到后会发生什么?
  4. swap 的价值和风险分别是什么?
  5. 为 Java 容器设计内存 limit 和 JVM 内存参数。