一、简介:为什么要懂虚拟内存?
应用视角:malloc/new 成功 ≠ 物理内存一定够用,延迟分配(Lazy Allocation)可能让程序在真正访问时触发 OOM(Out of Memory)。
运维视角:服务器突然卡顿,top 看到“还有内存”,但 swap 已飙高 →不懂虚拟内存就无从下手。
开发视角:写实时/低延迟程序,需要锁页(mlock)、禁止 swap,否则一次缺页中断就是毫秒级抖动。
掌握虚拟内存 = 能回答:
“我的进程到底用了多少内存?”、“free 与 top 数据为什么不一致?”、“swap 到底关不关?”
二、核心概念:5 张图看懂“虚-实-换”
| 概念 | 一句话 | 本文速记 |
|---|---|---|
| 虚拟地址空间 | 每个进程 4 GB(64 位 128 TB)连续假象 | cat /proc/PID/maps |
| 物理页(Page) | 4 KB 最小单元,全局唯一 | page_to_pfn() |
| 页表(Page Table) | 虚拟页 → 物理页映射,MMU 硬件走路 | MMU |
| 交换分区(Swap) | 冷页换出到磁盘,腾出物理内存 | /proc/swaps |
| 缺页中断(Page Fault) | 访问尚未映射物理页的虚拟页 → 内核处理 | vmstat的pf |
一句话总结:
虚拟内存 = 虚拟地址空间 + 页表 + 物理页帧 + 交换机制 + 缺页中断处理
三、环境准备:10 分钟搭好实验沙箱
系统
Ubuntu 20.04+ / CentOS 8+(内核 ≥5.4 即可)工具
util-linux(含 vmstat)stress-ng(内存压测)smem(PSS 统计)
一键安装
# Ubuntu/Debian sudo apt update && sudo apt install -y util-linux stress-ng smem # CentOS/RHEL sudo dnf install -y util-linux stress-ng smem实验目录
mkdir -p ~/mem-lab && cd ~/mem-lab
四、实际案例与步骤:从 0 到 1 会看、会压、会调
每个脚本均可直接复制,保存后
chmod +x xxx.sh && ./xxx.sh跑通。
4.1 快速上手:vmstat 1 秒级监控
#!/usr/bin/env bash # file: 01-vmstat-basic.sh echo "每 1 秒采样,输出 10 次" vmstat 1 10输出列说明(先记核心 6 列)
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa stswpd:已用 swap(KB)free:空闲物理内存si/so:每秒换入/换出页(>0 代表 swap 活跃)us/sy/id/wa:CPU 使用分布,wa 高常伴随 swap 风暴
4.2 压力测试:制造内存瓶颈观察指标
#!/usr/bin/env bash # file: 02-stress.sh # 消耗 2 GB 物理内存,持续 30 秒 stress-ng --vm 2 --vm-bytes 1G --timeout 30s --vm-keep并行终端监控
vmstat 1 | tee vmstat-stress.log现象:
free骤降,cache被回收si/so开始 >0,若内存不足swap 飙高wa列升高 → 等待换页完成,系统卡顿
4.3 进程视角:PSS、RSS、USS 区别
#!/usr/bin/env bash # file: 03-smem.sh # 以 nginx 为例 sudo smem -p | grep -E 'nginx|PID'输出
PID PSS RSS USS Command 1234 1.2M 2.0M 0.8M nginx: masterPSS(Proportional Share)= 共享页按比例分摊,最贴近“真实占用”
RSS含共享库,重复计算
USS不含共享,单进程独占
4.4 调优实战:三步缩小 swap 风暴
场景:桌面 Ubuntu 卡顿,vmstat 看到so>500 KB/s
①临时降低 swap 倾向
# 0=尽量不用 swap,100=积极用 echo 10 | sudo tee /proc/sys/vm/swappiness②永久写入
echo "vm.swappiness=10" >> /etc/sysctl.conf sudo sysctl -p③锁页(可选)
数据库/实时进程可在代码里调用
mlockall(MCL_CURRENT | MCL_FUTURE);或 systemd 单元加
LimitMEMLOCK=infinity
验证:再次压测,si/so保持 0,卡顿消失。
4.5 缺页中断统计:sar 互补验证
# 安装 sysstat sudo apt install -y sysstat # 每 2 秒统计缺页次数 sar -B 2 10关键指标:
pgscank/skswapd 回收页pgfault/s总缺页(Major + Minor)majflt/sMajor Fault→ 需磁盘 IO,性能杀手
五、常见问题与解答(FAQ)
| 问题 | 现象 | 排查 & 解决 |
|---|---|---|
free -m可用内存很少,但si/so=0 | 被 cache 占用 | 看available列,不必手动释放 |
stress-ng报out of memory但free还有 | 被vm.max_map_count限制 | sysctl vm.max_map_count=262144 |
| 关闭 swap 后系统卡死 | 无 swap 且内存耗尽 → OOM-killer 大量杀进程 | 不要直接 swapoff,降低 swappiness 更稳 |
| 同一进程 RSS 远大于 PSS | 共享库重复计算 | 以PSS为准做容量规划 |
| 容器内存超过 limit 被 OOM | 看/sys/fs/cgroup/memory/memory.stat | 对比cachevsrss决定是否缩小cache |
六、实践建议与最佳实践
监控“铁三角”
vmstat 1+sar -B 2+smem -p同时看,避免单指标误判。Swappiness 不是越低越好
数据库/实时:10
通用云主机:30(默认)
桌面系统:60 更流畅提前触发 OOM-killer 演练
echo 1 > /proc/sys/kernel/sysrq echo f > /proc/sysrq-trigger观察
dmesg杀进程顺序,验证重要服务是否被oom_score_adj保护。HugePage 减少 Major Fault
大内存应用(Redis、Oracle)开启vm.nr_hugepages=2048,页表项更少,缺页次数下降 30%+。systemd 资源限制
单元文件加:MemoryMax=4G MemorySwapMax=0硬封顶物理+swap,防止“一进程打满整机”。
七、总结:一张脑图带走全部要点
Linux 虚拟内存 ├─ 虚拟地址 → 页表 → 物理页 ├─ 监控:vmstat 1 看 si/so wa ├─ 压测:stress-ng --vm ├─ 评估:smem 看 PSS ├─ 调优:swappiness / HugePage / mlock └─ 排错:Major Fault → sar -B理解虚拟内存,你就拥有了:
容量规划时不再被“RSS”忽悠,用 PSS 算真实占用
性能调优时一眼识别 swap 风暴,用
vmstat秒级定位实时系统中合理配置
mlock+HugePage,把毫秒级抖动压到微秒级
立刻打开终端,跑一遍本文脚本,把输出截图存档——下次面试或生产排错,你也能5 分钟内给出量化结论,让“内存不足”不再靠拍脑袋!