当前位置:首页>Linux>趣话Linux Command②:Memory篇——你以为你在看内存,其实你在看错觉

趣话Linux Command②:Memory篇——你以为你在看内存,其实你在看错觉

  • 2026-10-04 01:46:35
趣话Linux Command②:Memory篇——你以为你在看内存,其实你在看错觉

本文不教你“怎么看内存占用”,而是讲清楚:

为什么 Linux 内存相关命令,从一开始就“故意让人看不懂”。


一、一个反直觉的现象

为什么 Linux 的 free 总是看起来“快没内存了”?

开篇直接用所有人都踩过的坑:

问题抛出:

  • 只跑了几个服务
  • free
     却只剩几百 MB
  • 但系统明明跑得很流畅

结论先行:

这不是 Linux 算错了,而是你理解错了“什么叫内存被用了”。

二、历史背景

UNIX 从一开始,就没打算让“空闲内存”存在

这一节讲设计动机,不是命令。

1️. 早期 UNIX 的现实约束

  • 内存极其昂贵
  • 磁盘慢到令人发指
  • 进程切换成本高

工程师的共识:

闲着的内存,是浪费。

2. page cache 的诞生

Linux 内存的真实目标不是:

  • “给进程用多少”

而是:

  • 尽可能把磁盘内容提前搬进内存

于是:

  • 文件读 → page cache
  • 程序启动 → page cache
  • shell → page cache

这直接决定了:

  • free 内存注定接近 0

第一个命令:free

它到底在“统计什么”?

这一节把 free 拆穿。

1️. free 的原始语义

  • 它统计的是:
    • 没被任何用途标记的 page
  • 而不是:
    • “还能不能用的内存”

2️.Buff/Cache 的真实含义

  • Buffer:块设备元数据; free命令把buffer和cache统计在一起了,要分开了解释,比较容易绕。缓冲区(Buffer)是用于临时存储待写入的块数据到,通常容量不大(约为20MB)。通过这种方式,操作系统内核能够将分散的写入操作集中起来,统一进行优化处理。例如,将多次零散的小规模写入合并为一次大规模写入等。

Buffer vs Cache

  • Cache: linux维护四种不同类型cache

    1.page cache(页缓存),缓存文件的实际内容,以数据块形式,Linux通常以内存页形式维护(默认为4KB);

     2. i-node cache(i节点缓存),缓存最近访问的文件的i-node(文件的元数据meta data,如权限、大小、时间戳、数据块位置等 );

当用户执行 ls -l或程序需要读取文件属性时,内核会先检查i-node Cache

     3. buffer cache(缓冲区缓存),与块设备交互,缓存最近使用的磁盘元数据块;

在早期Linux内核中,Buffer Cache是一个独立组件,专门缓存磁盘扇区数据。但在现代Linux内核(2.4版本以后)中,Buffer Cache和Page Cache已经合并 。现在,Buffer Cache可以看作是Page Cache的一部分或一种实现细节:当缓存文件数据时,使用Page Cache;当需要缓存底层的磁盘块(特别是文件系统元数据)时,这部分功能由改进后的Buffer Cache机制处理,但它不再独立存在,而是整合在Page Cache的框架内。

     4. directory cache(目录缓存),缓存目录项(包含文件名和对应的inode号),其主要作用是加速文件路径的解析过程。

  • 它们的共同点都是被OS管理,都可以被立即回收

但在 free 里,它们被算作“已用”。

3️. available 的出现,是一次认错

  • Linux 3.14 之后引入
  • 表示:  在不 swap、不 OOM (想了解OOM直接跳OOM Killer章节)的前提下,系统还能给你多少

这是一个典型的:

向历史妥协,又不敢破坏兼容性的产物


第二个命令:vmstat

为什么它不是“看内存”,而是“看行为”?

vmstat 1 

1️. vmstat 名字的误导性

  • vm = virtual memory
  • 但输出里:
    • 内存只是很小一部分

2️. 你真正该看的字段

si / so:swap in / outbi / bo:block IOin/ cs:interrupt and context switcher

vmstat 的设计哲学:

内存问题,永远不是静态的,是时间序列问题。

这个命令很强大,且用且珍惜。


第三个命令:top

为什么 RES / VIRT 几乎永远对不上?

1️. VIRT(virtual memory):一场“合法的欺骗”

  • 包括:
    • mmap
    • 共享库
    • 还没用到的地址空间

它回答的不是:

“你用了多少内存”

而是:

“你可能最多能用到多少”

2️. RES(Resident memory):你以为的“真实内存”,也不完全真实

  • 包含:
    • page cache 映射
    • 共享页的重复统计

Top命令注解

所以:

  • 把 VIRT和RES 相加,一定 > 物理内存

一个专门为“补锅”而生的命令:smem

为什么它存在?

因为:

  • RSS 不可信
  • VIRT 更不可信
  • Linux 没法完美统计“进程真实内存”

smem 的方案:

  • Resident Set Size RSS(常驻内存集):
    进程使用的共享库等内存被全额计入;
快速查看进程大致的内存占用,是 top、ps等工具的默认指标之一
  • Proportional Set Size PSS(比例驻留内存集):
    共享内存被均分给所有使用它的进程;
评估系统整体内存压力,是 Android 等系统决定终止哪个进程以释放内存的关键依据
  • Unique Set Size USS(唯一驻留内存集):
    共享内存不包含,只计算进程私有的内存;
  • Linux承认PSS:  这只是一个近似值

这是 Linux 内存世界里非常罕见的“坦诚”。

知道了这么多,你还要知道:

它其实还很好玩,可以有图有真相:

smem --pie name -s uss

smem 饼状图

smem --bar name -s uss

smem 柱状图


OOM Killer

Linux 为什么宁愿杀进程,也不让系统慢慢死?

这一节是情绪记忆点。

1️. overcommit 是默认策略

  • 承诺多于实际
  • 赌的是:
    • 大部分程序不会真用完

2️. swap 到死 vs 直接杀

Linux 的选择:

宁愿让一个进程死,也不让所有进程慢。

3️. OOM score 的“冷酷数学”

  • 哪个占得多  (RSS=anon-rss+file-rss ,RSS上升最快的进程,会死得最快)
  • 哪个最不重要
  • 就先杀谁
Memory Allocation
Importance for OOM Killer
这个“杀手”不太冷
anon-rss 进程独占的物理内存,如堆(heap)和栈(stack)
极高。这是OOM Killer最关心的“硬”内存消耗,无法被回收。
已经实际消耗掉的、无法再给别人的“食材”,比如已经用掉的油和炒好的菜。
file-rss 被映射到物理内存中的文件内容,如程序代码、共享库。
较低。这部分内存在紧张时可以被回收(丢弃后能从文件重新读取),因此对OOM的压力较小。
那些还未拆封的“预制菜”或“罐头”,在厨房空间不足时可以先挪出去。


一个完整的小故事:

当你敲下 free,内核到底在干嘛?

串一次完整路径:

  1. free 读 /proc/meminfo
  2. /proc
     是内核视角的投影
  3. 数据来自:
  • page allocator
  • memory zone
由于历史硬件原因,系统物理内存被划分为不同的区域(如 DMA、Normal、HighMem等)。/proc/meminfo中的 MemTotal就是对这些区域容量进行统计汇总的结果
    • LRU
  • 你看到的是:内核“此刻的主观认知”

下一篇我会写:Process & Scheduling(进程与调度),也许“你看到的进程,都是幻象”

参考文献:

  • The Linux Kernel's VFS Layer

  • SRP Technology - Difference between Buffer and Cache!! ..... | Facebook

  • Unleashing the Power of the Linux ‘top’ Command: A Comprehensive Guide | by Extio Technology | Medium

  • Smem Tool To Display Memory Usage More Accurate in Linux - GeeksforGeeks

最新文章

随机文章