这是《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
生产重点不是调整分数,而是:
- 控制内存上限;
- 修复泄漏;
- 设置水位告警;
- 保证关键服务有逃生容量;
- 通过编排重启策略恢复。
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
应用侧:
- Java 使用 heap dump 和 GC 日志;
- Go 使用 pprof;
- C/C++ 使用 valgrind、ASan 或 core;
- 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
处理:
- 定位大进程;
- 检查缓存上限;
- 重启或扩容前评估影响;
- 增加提前告警。
案例二:Java 容器 OOMKilled
kubectl describe pod <pod>
kubectl top pod <pod>
方向:
-Xmx过大;- 堆外内存或线程栈增长;
- limit 太小;
- 内存泄漏;
- 请求量超容量。
案例三:机器有 swap 但延迟抖动
vmstat 1
grep -E 'pswpin|pswpout' /proc/vmstat
如果 si/so 持续,说明应用工作集超过物理内存。应扩容或降低内存消耗,而不是只调大 swap。
本章小结
Linux 会把空闲内存用作缓存,判断内存压力要看 available、swap、major fault、回收和 OOM。进程内存要区分 VSS、RSS 和 PSS;容器要区分应用使用、页缓存和 cgroup hard limit。内存治理重点是容量预算、泄漏定位和水位告警。
思考题
free和available有什么区别?- RSS 高是否一定代表泄漏?
- cgroup memory.max 达到后会发生什么?
- swap 的价值和风险分别是什么?
- 为 Java 容器设计内存 limit 和 JVM 内存参数。