🐧 第五章 内存管理
📄 一句话概括

看内存不能只看【还剩多少】: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 秒实验

  1. free -h,对比 free 与 available 两列差多少——差出来的就是缓存。
  2. grep -E 'MemTotal|MemFree|MemAvailable|Cached' /proc/meminfo,注意单位是 kB。
  3. ps -eo pid,rss,vsz,comm --sort=-rss | head,看谁真的占着物理内存。

常见误区

  1. 【MemFree 小就是内存不够】。它是【完全没用的空闲】,缓存占着当然小。要看 MemAvailable。
  2. 【缓存占着内存是浪费】。缓存随时能吐出来,它让磁盘 IO 少了一个数量级。
  3. 【清理缓存能让系统变快】。恰恰相反——下一次读文件又得等磁盘。
自测三问(点开对答案) 1. MemFree 与 MemAvailable 差在哪? —— MemFree 只是完全没用的空闲;**MemAvailable 把可回收的缓存也算进来了**。**判断内存够不够,看后者。** 2. 为什么跑了几天什么都没干的机器,MemFree 也很小? —— **内核拿空闲内存去缓存文件了**。有进程要内存时随时能让出来,**这是健康状态。** 3. VSZ 与 RSS 该看哪个? —— 查内存占用看 **RSS**(真的在物理内存里的)。**VSZ 只是要了多少地址空间,没碰过的部分不占物理页。**

相关笔记