CPU 的运算离不开内存中的数据。理解 Linux 内存管理机制,是分析内存瓶颈、优化应用性能的前提。本文从虚拟内存的地址空间讲起,深入解析物理内存与交换分区的关系、Page Cache 与 Buffer Cache 的作用,以及 free 命令各项指标的真实含义。最后介绍 swappiness 和大页(HugePages)两个关键调优参数,帮你建立内存管理的完整认知。

一、虚拟内存:让每个进程都“拥有”整个地址空间
1.1 虚拟内存解决了什么问题?
在早期的计算机系统中,程序直接操作物理内存地址。这带来了两个致命问题:

地址冲突:两个程序如果同时运行,必须小心翼翼地划分物理内存,否则会互相覆盖。

内存不足:物理内存总是有限的,程序大小受限于可用物理内存。

虚拟内存(Virtual Memory) 的出现解决了这两个问题。它为每个进程提供了一个独立的、连续的虚拟地址空间(在 64 位系统上通常是 128TB),进程认为自己独占了整个内存。而实际上,这些虚拟地址通过页表(Page Table) 映射到物理内存的某个位置。

1.2 页表与 MMU:虚拟地址到物理地址的翻译
当 CPU 执行一条访问内存的指令时,它使用的是虚拟地址。这个地址需要被翻译成物理地址才能访问真正的内存芯片。翻译过程由硬件(MMU,内存管理单元)和内核共同完成:

CPU 将虚拟地址交给 MMU。

MMU 查询页表(Page Table) ,找到虚拟地址对应的物理页框号。

如果页表项有效,MMU 将虚拟地址转换为物理地址,完成访问。

如果页表项无效(该页不在物理内存中),触发缺页中断(Page Fault) ,内核从磁盘(swap 或文件)将数据加载到物理内存。

页表是分层组织的(Linux 目前支持五级页表),每一级页表负责索引下一级,最终指向物理页。这种分层设计既节省了页表本身占用的内存,又支持了大地址空间。

1.3 用户空间与内核空间
Linux 将虚拟地址空间划分为两部分:

用户空间:应用程序运行的地方,每个进程拥有独立的用户空间。

内核空间:操作系统内核运行的地方,所有进程共享同一个内核空间。

在 64 位 Linux 上,典型的划分是:用户空间占低 128TB,内核空间占高 128TB。用户进程不能直接访问内核空间,这种隔离保障了系统的安全性。

二、物理内存的两种角色:匿名页与文件页
根据用途,物理内存页可以分为两类,理解它们的区别是分析内存使用的关键:

类型	内容	存储位置	回收方式
文件页(File-backed Pages)	文件数据(程序代码、动态库、日志文件等)	磁盘上的文件	直接丢弃(文件可从磁盘重新读取)或写回(脏页)
匿名页(Anonymous Pages)	进程的堆、栈、写时复制数据等	无对应的磁盘文件	需交换到 Swap 分区后才能释放

文件页对应的是 Page Cache(页面缓存),用于缓存文件数据。匿名页则对应进程的私有内存。

三、Page Cache 与 Buffer Cache:Linux 的“内存加速器”
3.1 什么是 Page Cache?
Page Cache(页面缓存) 是 Linux 内核实现的一种主要磁盘缓存。它的核心思想是:将最近访问过的文件数据保留在物理内存中,下次需要时直接从内存读取,避免访问慢速磁盘。

当你 cat 一个文件时,文件内容被读入 Page Cache。

当你再次 cat 同一个文件时,数据直接从 Page Cache 返回,速度提升几个数量级。

当你写入文件时,数据先写入 Page Cache(标记为脏页),再由内核的 pdflush 线程异步写入磁盘。

Page Cache 可以被多个进程共享,从而提高内存利用率和系统响应速度。

3.2 什么是 Buffer Cache?
Buffer Cache(缓冲区缓存) 用于缓存磁盘块(block) 的元数据。它不缓存文件内容,而是缓存磁盘的块信息(如超级块、inode 表等),减少磁盘 I/O 操作。

在现代 Linux 内核中,Buffer Cache 和 Page Cache 已经合并管理,但 free 命令仍然保留了 buff/cache 列,将两者合并显示。

3.3 “空闲的内存就是浪费的内存”
这是理解 Linux 内存管理最重要的原则。Linux 会尽可能利用空闲物理内存来缓存文件数据,以提升系统性能。所以当你看到 free 命令显示内存使用率很高时,不必惊慌——大部分可能只是缓存,随时可以被回收分配给应用程序。

四、free 命令:读懂内存的“体检报告”
free 命令是最常用的内存查看工具。以 free -h(人类可读格式)为例:

$ free -h
              total        used        free      shared  buff/cache   available
Mem:           15Gi       2.1Gi       1.2Gi       123Mi        12Gi        13Gi
Swap:         2.0Gi       0.0Ki       2.0Gi

4.1 各列的含义
列	含义	解读
total	物理内存总量	硬件总内存
used	已使用的内存	total - free - buff/cache(约等于)
free	完全未使用的内存	空闲内存,不代表可用内存
shared	被共享内存使用的内存	多个进程共享的内存(如 tmpfs)
buff/cache	被 Buffer 和 Cache 使用的内存总和	可回收的缓存,不是真正被“占用”
available	真正可用的内存	最重要的指标,包含可回收的 cache

4.2 available vs free:哪个更重要?
available 是 Linux 内核估算的、在不触发交换(swap)的情况下,可以分配给新应用程序的内存总量。

它的计算公式大致为:

text
available ≈ free + 可回收的 buff/cache

💡 关键结论:看内存是否充足,请盯着 available 列,而不是 free 列。一个系统 free 只有几百 MB,但 available 还有几 GB,说明内存状态健康——那些“被占用”的内存只是缓存,随时可以释放给应用。

4.3 手动清理缓存(谨慎使用)
在生产环境中,通常不需要手动清理缓存,内核会自动管理。但如果为了测试,可以通过以下方式释放:

# 释放 Page Cache
echo 1 > /proc/sys/vm/drop_caches
# 释放 dentries 和 inodes
echo 2 > /proc/sys/vm/drop_caches
# 释放 Page Cache、dentries 和 inodes
echo 3 > /proc/sys/vm/drop_caches

⚠️ 警告:在生产环境执行 drop_caches 会导致所有缓存失效,瞬间增加磁盘 I/O,可能引起性能抖动。除非有明确测试目的,否则不要执行。

五、Swap:内存的“备胎”
当物理内存不足时,内核会将部分不常使用的内存页交换(swap) 到磁盘上的 Swap 分区或 Swap 文件。这相当于用磁盘空间“冒充”内存,让系统能在内存不足时继续运行。

5.1 Swap 的工作原理
Swap 的交换过程由内核的调页算法(Paging) 和交换技术(Swapping) 共同完成:

调页(Paging) :将内存中最近不常使用的页面(page) 换出到磁盘,把常使用的页面保留在内存中。

交换(Swapping) :将整个进程的内存映像换出到磁盘(在现代 Linux 中较少使用)。

5.2 何时使用 Swap?
vmstat 命令的 si(swap in)和 so(swap out)列可以监控 Swap 的使用情况:

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0  10240  50000  20000 300000    0    0    10    20  500 8000 25  5 68  2  0

si:每秒从磁盘交换到内存的页数(KB/秒)

so:每秒从内存交换到磁盘的页数(KB/秒)

关键原则:当 si 和 so 长期大于 0 时,说明系统内存不足,已经频繁使用 Swap。这会消耗大量磁盘 I/O 和 CPU 资源,严重影响性能。

💡 注意:swpd 列显示的是当前已使用的 Swap 总量(KB)。如果 swpd 很大但 si/so 为 0,说明历史上曾使用过 Swap,但当前没有交换活动——这通常不是问题。

5.3 swappiness:控制内核“用 Swap 的冲动”
swappiness 是 Linux 内核参数,控制系统主动使用 Swap 的倾向。取值范围是 0 到 100,默认值通常是 60。

值越高,内核越倾向于将不活跃的内存页交换到磁盘,以释放物理内存用于 Page Cache。

值越低,内核越倾向于保留内存中的匿名页,减少 Swap 使用。

查看当前值:

cat /proc/sys/vm/swappiness

临时调整:

sudo sysctl vm.swappiness=10

永久调整(写入 /etc/sysctl.conf):

text
vm.swappiness = 10
典型场景的 swappiness 建议值:

场景	建议值	理由
数据库服务器(MySQL/PostgreSQL)	1-10	数据库有自己的缓存机制,Swap 会导致性能急剧下降
容器/K8s 节点	10-30	容器通常有内存限制,过度 Swap 会影响调度
桌面系统 / 通用服务器	60(默认)	平衡内存与缓存
内存充足的系统	10-20	尽量使用物理内存

六、大页(HugePages)与透明大页(THP)
6.1 为什么需要大页?
Linux 默认的内存页大小是 4KB。对于内存密集型应用(如数据库),页表会变得非常庞大——一个使用 64GB 内存的进程,需要约 1600 万个页表项。这会消耗大量内存,并导致 TLB(Translation Lookaside Buffer,页表缓存) 频繁未命中,降低性能。

大页(HugePages) 将页面大小从 4KB 提升到 2MB(或 1GB),从而:

减少页表项数量(64GB 内存只需要约 3.2 万个页表项)。

减少 TLB 未命中,提升内存访问速度。

6.2 HugePages vs 透明大页(THP)
特性	HugePages	透明大页(THP)
管理方式	需管理员手动预留	内核自动管理
灵活性	低(预留后不可他用)	高(动态调整页面大小)
适用场景	明确需要大页的应用(如数据库)	通用场景
风险	无	可能导致内存碎片和性能问题

THP 的争议:THP 虽然简化了管理,但对于访问模式离散的负载(如数据库),可能因为频繁的页面合并和拆分导致性能下降甚至系统不稳定。许多数据库官方文档(如 MySQL、MongoDB)建议在生产环境禁用 THP。

禁用 THP:

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

七、小结
虚拟内存通过页表让每个进程拥有独立的地址空间,MMU 负责将虚拟地址翻译为物理地址。

Page Cache 和 Buffer Cache 利用空闲内存缓存文件数据,提升 I/O 性能。free 命令的 available 列才是真正可用的内存。

Swap 是内存的“备胎”,si/so 长期大于 0 说明内存不足。swappiness 控制内核使用 Swap 的倾向。

大页(HugePages) 减少页表开销和 TLB 未命中,但 THP 可能引入风险,需根据场景谨慎选择

更多推荐