看内存不能只看【还剩多少】:MemFree 几乎总是很小,那是缓存占了——真正的答案是 MemAvailable。而所有数字的底座都是【一页 4 KB】。
第 4 章比完 CPU 怎么分,这一章换战场比内存。先学会看账——账都看不懂,后面讲怎么分就无处落脚。
一、六个必须认识的数
MemTotal:物理内存总量,分母。 MemFree:完全没用的空闲——这个数在正常系统上总是很小,因为它不把【随时能回收的缓存】算进来。 MemAvailable:估算的可用量,把可回收的部分也算上了。判断内存紧不紧看它。 Buffers 与 Cached:给文件留的缓存。它们不是被吃掉了,是借出去了——有进程要内存,随时能吐出来。 SwapTotal 与 SwapFree:交换区大小与余量。都为 0 说明这台机器没配交换(容器里常见)。 Slab:内核自己用的那部分(各种缓存与结构体),用户看不见但确实占着内存。
二、为什么 MemFree 几乎总是很小
因为空闲的内存放着也是放着:内核会拿它去缓存文件,下次读同一个文件就不用等磁盘。 所以在一台跑了几天、什么也没干的服务器上,MemFree 也可能只剩几百 MB——这是健康状态,不是内存泄漏。 反过来说,MemFree 很大才值得查:要么刚重启,要么内存根本用不完。 一句话:看【可用】不看【空闲】。
三、所有数字的底座都是一页 4 KB
内存不是按字节发,是按页发。x86-64 上经典页大小是 4 KB。 于是所有统计都能换算成页数:总量 ÷ 4 KB = 总页数;页表项数也就等于页数。 这个换算在第 5.16 节会变成一个惊人的数字:单级页表要给整个虚拟地址空间建表,按一台 16 GB 的机器算就已经要 32 MB 的表。
四、进程维度:两个大小别搞混
VSZ(虚拟大小):这个进程一共【要】了多少地址空间。 RSS(常驻大小):真的躺在物理内存里的有多少。 前者通常远大于后者,因为要下来的地址空间里大片地方根本没碰过——没碰过就不分配物理页(5.13 详述)。 查内存泄漏看 RSS,看 VSZ 只会把自己吓一跳。
五、动手 Demo:内存的统计信息——一台机器有多少内存,谁在占
这一节真读一次本机内存(Windows 走系统调用,Linux 读 /proc/meminfo,两条路的数字含义一样),再按 4 KB 一页把它换算成页数。
import ctypes
class MSX(ctypes.Structure):
_fields_ = [("dwLength", ctypes.c_ulong),
("dwMemoryLoad", ctypes.c_ulong),
("ullTotalPhys", ctypes.c_ulonglong),
("ullAvailPhys", ctypes.c_ulonglong),
("ullTotalPageFile", ctypes.c_ulonglong),
("ullAvailPageFile", ctypes.c_ulonglong),
("ullTotalVirtual", ctypes.c_ulonglong),
("ullAvailVirtual", ctypes.c_ulonglong),
("ullAvailExtendedVirtual", ctypes.c_ulonglong)]
st = MSX()
st.dwLength = ctypes.sizeof(MSX)
ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(st))
PAGE = 4096
print("总量", st.ullTotalPhys // 1024 // 1024, "MB")
print("可用", st.ullAvailPhys // 1024 // 1024, "MB")
print("总页数", st.ullTotalPhys // PAGE)
print("单级页表要多大(MB)", st.ullTotalPhys // PAGE * 8 // 1024 // 1024)
实跑输出:
--------------------------------------------------------------------
内存的统计信息——一台机器有多少内存,谁在占
--------------------------------------------------------------------
① 真读一次本机(不装任何工具,直接问内核)
指标 含义 本机实测
---------------------------------------------------
物理内存总量 插了几条内存条 15.87 GB
当前可用 还能不能再分出去 4.73 GB
已被占用 总量减可用 11.14 GB
占用比例 内核算好的百分比 70%
提交上限 内存加页面文件的上限 16.01 GB
② 换成 Linux 的视角:这些数在 /proc/meminfo 里
/proc/meminfo 字段 对上本机哪个数 要不要一起看
--------------------------------------------------------------------
MemTotal 物理内存总量 分母
MemFree 完全没用的空闲 越小越好?不一定
MemAvailable 估算的可用(含可回收) **看这个才对**
Buffers/Cached 给文件留的缓存 紧张时可回收
SwapTotal/Free 交换区大小与余量 为 0 说明没配交换
③ 按页算一遍:内存是【一页一页】发的
算的是 公式 结果
--------------------------------------------------
页大小 x86-64 经典值 4 KB
总页数 物理内存 ÷ 4 KB 4160368 页
页表项总数 一页一项 4160368 项
页表本身要多大 页数 × 8 字节 32 MB
**最后一行就是 5.16 要讲的问题**:单级页表要给整个虚拟地址空间建表,
**按这台机器的内存算就已经要 32 MB**,而虚拟空间比物理内存大得多。
**所以真要四级页表,而且是【用到才建】。**
④ 一句提醒
**【空闲内存少】不是病**:Linux 会把暂时没用的内存拿去做缓存,
**看起来【没内存了】,其实随时能吐出来。**
**判断内存紧不紧,看 MemAvailable 和有没有开始用 Swap,不要看 MemFree。**
这台机器的实测:总量 15.87 GB、可用 4.11 GB、占用 74%——换成页算是 416 万页,单级页表要 32 MB。 Linux 上同一个数字在 /proc/meminfo 的 MemTotal 里,但要注意它给的单位是 kB,而且 MemFree 不是【可用】。 最容易被误读的一行是 MemFree:它小得离谱是正常的,因为缓存占着;要看 MemAvailable。
📋 Linux 上最快的看法:free -h——第一行是内存,available 那一列才是答案。free 输出里的 buff/cache 就是【借出去的那部分】。
30 秒实验
free -h,对比 free 与 available 两列差多少——差出来的就是缓存。grep -E 'MemTotal|MemFree|MemAvailable|Cached' /proc/meminfo,注意单位是 kB。ps -eo pid,rss,vsz,comm --sort=-rss | head,看谁真的占着物理内存。
常见误区
- 【MemFree 小就是内存不够】。它是【完全没用的空闲】,缓存占着当然小。要看 MemAvailable。
- 【缓存占着内存是浪费】。缓存随时能吐出来,它让磁盘 IO 少了一个数量级。
- 【清理缓存能让系统变快】。恰恰相反——下一次读文件又得等磁盘。
自测三问(点开对答案)
1. MemFree 与 MemAvailable 差在哪? —— MemFree 只是完全没用的空闲;**MemAvailable 把可回收的缓存也算进来了**。**判断内存够不够,看后者。** 2. 为什么跑了几天什么都没干的机器,MemFree 也很小? —— **内核拿空闲内存去缓存文件了**。有进程要内存时随时能让出来,**这是健康状态。** 3. VSZ 与 RSS 该看哪个? —— 查内存占用看 **RSS**(真的在物理内存里的)。**VSZ 只是要了多少地址空间,没碰过的部分不占物理页。**- ☐ 5.2 内存不足
相关笔记
- 吞吐量与延迟 — 上一个被权衡的资源
- 内存不足 — 不够了怎么办
- 多级页表 — 那 32 MB 页表怎么被砍掉
- 🐧 Linux是怎样工作的 · 索引