Linux内存管理(Memory Management,简称MM)是一套庞大、分层协作的内核子系统,统一负责虚拟地址空间、页表、缺页异常、内存分配、页面回收、Swap、Page Cache、OOM等全部内存相关逻辑。决定进程可访问的地址范围、虚拟地址映射到哪个物理页、物理内存如何分配,内存压力时如何回收页面,回答「内存在哪里、归谁、是否驻留内存」。
一、内存管理整体分层链路
以一行简单C++动态内存分配代码auto* p = new char[1024];为例,内存分配链路是用户态分配器 → Linux内核MM子系统 → 物理内存硬件三层架构。
需要注意,一次具体的
new/malloc并不一定进入内核:如果用户态分配器能够从tcache、Arena等已有空闲内存中满足请求,则不会触发brk/mmap系统调用。
ptmalloc 在用户态预先缓存大块内存,应用调用 malloc 时优先从缓存拿,不够才向内核brk/mmap申请内存;free 不会直接还给内核,放回缓存供后续复用
#mermaid-svg-rk9lKLHe6H97V2L1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rk9lKLHe6H97V2L1 .error-icon{fill:#552222;}#mermaid-svg-rk9lKLHe6H97V2L1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rk9lKLHe6H97V2L1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rk9lKLHe6H97V2L1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rk9lKLHe6H97V2L1 .marker.cross{stroke:#333333;}#mermaid-svg-rk9lKLHe6H97V2L1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rk9lKLHe6H97V2L1 p{margin:0;}#mermaid-svg-rk9lKLHe6H97V2L1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 .cluster-label text{fill:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 .cluster-label span{color:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 .cluster-label span p{background-color:transparent;}#mermaid-svg-rk9lKLHe6H97V2L1 .label text,#mermaid-svg-rk9lKLHe6H97V2L1 span{fill:#333;color:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 .node rect,#mermaid-svg-rk9lKLHe6H97V2L1 .node circle,#mermaid-svg-rk9lKLHe6H97V2L1 .node ellipse,#mermaid-svg-rk9lKLHe6H97V2L1 .node polygon,#mermaid-svg-rk9lKLHe6H97V2L1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rk9lKLHe6H97V2L1 .rough-node .label text,#mermaid-svg-rk9lKLHe6H97V2L1 .node .label text,#mermaid-svg-rk9lKLHe6H97V2L1 .image-shape .label,#mermaid-svg-rk9lKLHe6H97V2L1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-rk9lKLHe6H97V2L1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rk9lKLHe6H97V2L1 .rough-node .label,#mermaid-svg-rk9lKLHe6H97V2L1 .node .label,#mermaid-svg-rk9lKLHe6H97V2L1 .image-shape .label,#mermaid-svg-rk9lKLHe6H97V2L1 .icon-shape .label{text-align:center;}#mermaid-svg-rk9lKLHe6H97V2L1 .node.clickable{cursor:pointer;}#mermaid-svg-rk9lKLHe6H97V2L1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rk9lKLHe6H97V2L1 .arrowheadPath{fill:#333333;}#mermaid-svg-rk9lKLHe6H97V2L1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rk9lKLHe6H97V2L1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rk9lKLHe6H97V2L1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rk9lKLHe6H97V2L1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rk9lKLHe6H97V2L1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rk9lKLHe6H97V2L1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rk9lKLHe6H97V2L1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rk9lKLHe6H97V2L1 .cluster text{fill:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 .cluster span{color:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rk9lKLHe6H97V2L1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rk9lKLHe6H97V2L1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-rk9lKLHe6H97V2L1 .icon-shape,#mermaid-svg-rk9lKLHe6H97V2L1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rk9lKLHe6H97V2L1 .icon-shape p,#mermaid-svg-rk9lKLHe6H97V2L1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rk9lKLHe6H97V2L1 .icon-shape .label rect,#mermaid-svg-rk9lKLHe6H97V2L1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rk9lKLHe6H97V2L1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rk9lKLHe6H97V2L1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rk9lKLHe6H97V2L1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-rk9lKLHe6H97V2L1 .app>*{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .app span{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .app tspan{fill:#0d47a1!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .runtime>*{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .runtime span{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .runtime tspan{fill:#1b5e20!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .user>*{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .user span{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .user tspan{fill:#e65100!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .kernel>*{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:1.5px!important;color:#4a148c!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .kernel span{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:1.5px!important;color:#4a148c!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .kernel tspan{fill:#4a148c!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .phys>*{fill:#e0f7fa!important;stroke:#00acc1!important;stroke-width:1.5px!important;color:#006064!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .phys span{fill:#e0f7fa!important;stroke:#00acc1!important;stroke-width:1.5px!important;color:#006064!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .phys tspan{fill:#006064!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .ram>*{fill:#ffebee!important;stroke:#e53935!important;stroke-width:2px!important;color:#b71c1c!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .ram span{fill:#ffebee!important;stroke:#e53935!important;stroke-width:2px!important;color:#b71c1c!important;}#mermaid-svg-rk9lKLHe6H97V2L1 .ram tspan{fill:#b71c1c!important;} new / delete
malloc / free
brk / mmap 系统调用
C++ Application
C/C++ Runtime
Userspace Memory Allocator
glibc ptmalloc
tcache
线程本地缓存
arena
多线程独立分配域
bins / free chunk
空闲块链表
本地空闲内存耗尽
Linux Kernel Memory Management
mm_struct
进程地址空间描述符
VMA
虚拟内存区域
Page Table
多级页表
Page Fault
缺页异常处理
Page Allocator
物理页分配器
Page Reclaim
页面回收
Swap
Physical Memory
NUMA Node
NUMA 节点
Zone
内存域
PCP
Per-CPU Pageset
Buddy Allocator
伙伴系统
RAM
硬件侧地址转换链路:
#mermaid-svg-Cbg1GGc0ZEvKwCEk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .error-icon{fill:#552222;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .marker.cross{stroke:#333333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk p{margin:0;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .cluster-label text{fill:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .cluster-label span{color:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .cluster-label span p{background-color:transparent;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .label text,#mermaid-svg-Cbg1GGc0ZEvKwCEk span{fill:#333;color:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .node rect,#mermaid-svg-Cbg1GGc0ZEvKwCEk .node circle,#mermaid-svg-Cbg1GGc0ZEvKwCEk .node ellipse,#mermaid-svg-Cbg1GGc0ZEvKwCEk .node polygon,#mermaid-svg-Cbg1GGc0ZEvKwCEk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .rough-node .label text,#mermaid-svg-Cbg1GGc0ZEvKwCEk .node .label text,#mermaid-svg-Cbg1GGc0ZEvKwCEk .image-shape .label,#mermaid-svg-Cbg1GGc0ZEvKwCEk .icon-shape .label{text-anchor:middle;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .rough-node .label,#mermaid-svg-Cbg1GGc0ZEvKwCEk .node .label,#mermaid-svg-Cbg1GGc0ZEvKwCEk .image-shape .label,#mermaid-svg-Cbg1GGc0ZEvKwCEk .icon-shape .label{text-align:center;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .node.clickable{cursor:pointer;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .arrowheadPath{fill:#333333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Cbg1GGc0ZEvKwCEk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cbg1GGc0ZEvKwCEk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Cbg1GGc0ZEvKwCEk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .cluster text{fill:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .cluster span{color:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Cbg1GGc0ZEvKwCEk rect.text{fill:none;stroke-width:0;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .icon-shape,#mermaid-svg-Cbg1GGc0ZEvKwCEk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .icon-shape p,#mermaid-svg-Cbg1GGc0ZEvKwCEk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .icon-shape .label rect,#mermaid-svg-Cbg1GGc0ZEvKwCEk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cbg1GGc0ZEvKwCEk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Cbg1GGc0ZEvKwCEk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .addr>*{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .addr span{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .addr tspan{fill:#0d47a1!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .hw>*{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:1.5px!important;color:#4a148c!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .hw span{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:1.5px!important;color:#4a148c!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .hw tspan{fill:#4a148c!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .hit>*{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .hit span{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .hit tspan{fill:#1b5e20!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .miss>*{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .miss span{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .miss tspan{fill:#e65100!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .out>*{fill:#ffebee!important;stroke:#e53935!important;stroke-width:2px!important;color:#b71c1c!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .out span{fill:#ffebee!important;stroke:#e53935!important;stroke-width:2px!important;color:#b71c1c!important;}#mermaid-svg-Cbg1GGc0ZEvKwCEk .out tspan{fill:#b71c1c!important;} Virtual Address
虚拟地址
MMU
内存管理单元(硬件)
TLB Hit
TLB 命中
TLB Miss
TLB 未命中
Physical Address
物理地址
Page Walk
硬件遍历多级页表
三层体系总结:
- 用户态 :
malloc/free,ptmalloc/tcache/arena; - 内核态:Linux MM子系统,VMA、页表、缺页、页面分配与回收;
- 硬件:MMU、TLB、物理内存RAM。
注:业内所说Linux Memory Manager,特指内核内部MM子系统,不含glibc用户态分配器。
二、进程虚拟地址空间
2.1 Virtual Address Space
Linux不允许进程直接操作物理地址,每个进程都拥有独立的虚拟地址空间。64位进程典型虚拟地址布局:

虚拟内存价值:
- 进程地址空间隔离,一个进程无法直接访问另一进程内存;
- 屏蔽复杂物理内存拓扑(NUMA、内存空洞);
- 支持按需分页(Demand Paging);
- 支持进程间共享内存、文件映射;
- 实现虚拟连续,物理可不连续。
2.2 mm_struct 进程地址空间描述符
内核使用struct mm_struct描述一个进程完整虚拟地址空间,是进程内存顶层管理对象。
#mermaid-svg-601A21f0BFTjRuhW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-601A21f0BFTjRuhW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-601A21f0BFTjRuhW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-601A21f0BFTjRuhW .error-icon{fill:#552222;}#mermaid-svg-601A21f0BFTjRuhW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-601A21f0BFTjRuhW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-601A21f0BFTjRuhW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-601A21f0BFTjRuhW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-601A21f0BFTjRuhW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-601A21f0BFTjRuhW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-601A21f0BFTjRuhW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-601A21f0BFTjRuhW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-601A21f0BFTjRuhW .marker.cross{stroke:#333333;}#mermaid-svg-601A21f0BFTjRuhW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-601A21f0BFTjRuhW p{margin:0;}#mermaid-svg-601A21f0BFTjRuhW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-601A21f0BFTjRuhW .cluster-label text{fill:#333;}#mermaid-svg-601A21f0BFTjRuhW .cluster-label span{color:#333;}#mermaid-svg-601A21f0BFTjRuhW .cluster-label span p{background-color:transparent;}#mermaid-svg-601A21f0BFTjRuhW .label text,#mermaid-svg-601A21f0BFTjRuhW span{fill:#333;color:#333;}#mermaid-svg-601A21f0BFTjRuhW .node rect,#mermaid-svg-601A21f0BFTjRuhW .node circle,#mermaid-svg-601A21f0BFTjRuhW .node ellipse,#mermaid-svg-601A21f0BFTjRuhW .node polygon,#mermaid-svg-601A21f0BFTjRuhW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-601A21f0BFTjRuhW .rough-node .label text,#mermaid-svg-601A21f0BFTjRuhW .node .label text,#mermaid-svg-601A21f0BFTjRuhW .image-shape .label,#mermaid-svg-601A21f0BFTjRuhW .icon-shape .label{text-anchor:middle;}#mermaid-svg-601A21f0BFTjRuhW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-601A21f0BFTjRuhW .rough-node .label,#mermaid-svg-601A21f0BFTjRuhW .node .label,#mermaid-svg-601A21f0BFTjRuhW .image-shape .label,#mermaid-svg-601A21f0BFTjRuhW .icon-shape .label{text-align:center;}#mermaid-svg-601A21f0BFTjRuhW .node.clickable{cursor:pointer;}#mermaid-svg-601A21f0BFTjRuhW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-601A21f0BFTjRuhW .arrowheadPath{fill:#333333;}#mermaid-svg-601A21f0BFTjRuhW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-601A21f0BFTjRuhW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-601A21f0BFTjRuhW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-601A21f0BFTjRuhW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-601A21f0BFTjRuhW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-601A21f0BFTjRuhW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-601A21f0BFTjRuhW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-601A21f0BFTjRuhW .cluster text{fill:#333;}#mermaid-svg-601A21f0BFTjRuhW .cluster span{color:#333;}#mermaid-svg-601A21f0BFTjRuhW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-601A21f0BFTjRuhW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-601A21f0BFTjRuhW rect.text{fill:none;stroke-width:0;}#mermaid-svg-601A21f0BFTjRuhW .icon-shape,#mermaid-svg-601A21f0BFTjRuhW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-601A21f0BFTjRuhW .icon-shape p,#mermaid-svg-601A21f0BFTjRuhW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-601A21f0BFTjRuhW .icon-shape .label rect,#mermaid-svg-601A21f0BFTjRuhW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-601A21f0BFTjRuhW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-601A21f0BFTjRuhW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-601A21f0BFTjRuhW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-601A21f0BFTjRuhW .proc>*{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-601A21f0BFTjRuhW .proc span{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-601A21f0BFTjRuhW .proc tspan{fill:#0d47a1!important;}#mermaid-svg-601A21f0BFTjRuhW .core>*{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:2px!important;color:#4a148c!important;}#mermaid-svg-601A21f0BFTjRuhW .core span{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:2px!important;color:#4a148c!important;}#mermaid-svg-601A21f0BFTjRuhW .core tspan{fill:#4a148c!important;}#mermaid-svg-601A21f0BFTjRuhW .vma>*{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-601A21f0BFTjRuhW .vma span{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-601A21f0BFTjRuhW .vma tspan{fill:#e65100!important;}#mermaid-svg-601A21f0BFTjRuhW .pt>*{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-601A21f0BFTjRuhW .pt span{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-601A21f0BFTjRuhW .pt tspan{fill:#1b5e20!important;}#mermaid-svg-601A21f0BFTjRuhW .etc>*{fill:#eceff1!important;stroke:#607d8b!important;stroke-width:1.5px!important;color:#263238!important;}#mermaid-svg-601A21f0BFTjRuhW .etc span{fill:#eceff1!important;stroke:#607d8b!important;stroke-width:1.5px!important;color:#263238!important;}#mermaid-svg-601A21f0BFTjRuhW .etc tspan{fill:#263238!important;} Process
进程
mm_struct
进程地址空间描述符
VMA
代码段
VMA
数据段
VMA
堆
VMA
栈
VMA
mmap 映射区
Page Tables
多级页表
...
同一进程内所有线程共享同一个mm_struct,这就是同进程线程可以直接访问对方堆、全局变量的根本原因。
线程栈属于虚拟地址空间,但每个线程拥有独立栈VMA。
2.3 VMA(vm_area_struct)
内核使用VMA(Virtual Memory Area,struct vm_area_struct)描述一段连续且映射属性一致的虚拟地址区域 。这里的映射属性不仅包括读、写、执行权限,还包括文件映射关系、页保护属性以及其他VMA相关策略。
VMA描述进程中哪些虚拟地址范围是合法的,以及这些区域应具有怎样的映射属性和访问权限;
每段VMA记录vm_start、vm_end、vm_flags读写执行权限、vm_page_prot页保护属性、vm_file映射文件等信息。
text
mm_struct
│
├── VMA:text代码段
├── VMA:heap堆
├── VMA:shared library共享库
├── VMA:mmap文件映射
└── VMA:stack栈
区分
mm_struct:整个进程虚拟地址空间的顶层容器;vm_area_struct:一段连续、属性一致的虚拟地址区域。
三、虚拟地址到物理地址映射:页表、MMU、TLB
3.1 Page Table(多级页表)
页表保存地址转换所需的映射信息,供MMU完成VA→PA转换。正常的已映射内存访问通常不会查询VMA;当发生Page Fault等异常时,内核才会根据VMA判断该地址是否合法以及应如何建立或修复页表映射。
text
PGD → P4D → PUD → PMD → PTE → Physical Page Frame
不同硬件架构不一定启用全部五级,不需要的层级会通过Page Table Folding折叠,简化为更少级数。
3.2 PTE(Page Table Entry)
PTE是页表最底层条目,虚拟地址被切分为多级索引 + 页内偏移 。
PTE中包含物理页帧号、Present位、读写权限、用户/内核态标记、Accessed访问位、Dirty脏位等元数据。
地址转换公式:
VA = 虚拟页基地址 + 页内Offset
PA = 物理页基地址 + 页内Offset
页表以Page为最小单位维护虚拟地址到物理页帧映射与访问权限。
3.3 MMU
MMU(Memory Management Unit)是CPU内部硬件单元,真正执行地址转换。普通内存访问不需要内核参与地址翻译,全部由CPU硬件完成。只有PTE标记无效、权限非法时,MMU触发异常,陷入内核处理Page Fault。
3.4 TLB(Translation Lookaside Buffer)
TLB(Translation Lookaside Buffer)是CPU内部用于加速地址转换的高速缓存,缓存近期使用过的虚拟页→物理页的地址转换结果以及相关权限属性,从而避免每次内存访问都执行多级页表遍历。
#mermaid-svg-Xj8Fcz8Fb9UfLc6w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .error-icon{fill:#552222;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .marker.cross{stroke:#333333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w p{margin:0;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .cluster-label text{fill:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .cluster-label span{color:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .cluster-label span p{background-color:transparent;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .label text,#mermaid-svg-Xj8Fcz8Fb9UfLc6w span{fill:#333;color:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node rect,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node circle,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node ellipse,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node polygon,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .rough-node .label text,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node .label text,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .image-shape .label,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .icon-shape .label{text-anchor:middle;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .rough-node .label,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node .label,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .image-shape .label,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .icon-shape .label{text-align:center;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node.clickable{cursor:pointer;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .arrowheadPath{fill:#333333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .cluster text{fill:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .cluster span{color:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w rect.text{fill:none;stroke-width:0;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .icon-shape,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .icon-shape p,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .icon-shape .label rect,#mermaid-svg-Xj8Fcz8Fb9UfLc6w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .addr>*{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .addr span{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .addr tspan{fill:#0d47a1!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .hw>*{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:1.5px!important;color:#4a148c!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .hw span{fill:#f3e5f5!important;stroke:#8e24aa!important;stroke-width:1.5px!important;color:#4a148c!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .hw tspan{fill:#4a148c!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .hit>*{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .hit span{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .hit tspan{fill:#1b5e20!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .miss>*{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .miss span{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .miss tspan{fill:#e65100!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .out>*{fill:#ffebee!important;stroke:#e53935!important;stroke-width:2px!important;color:#b71c1c!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .out span{fill:#ffebee!important;stroke:#e53935!important;stroke-width:2px!important;color:#b71c1c!important;}#mermaid-svg-Xj8Fcz8Fb9UfLc6w .out tspan{fill:#b71c1c!important;} Virtual Address
虚拟地址
TLB 查询
TLB Hit
TLB 命中
TLB Miss
TLB 未命中
Physical Address
物理地址
Page Walk
硬件遍历多级页表
三者关系:
- Page Table :存储虚拟地址转换所需的页表条目;对于当前未驻留或尚未建立物理映射的虚拟页,对应叶子条目可能不存在或处于Non-Present状态;
- TLB :CPU内部的地址转换缓存,保存近期使用的虚拟页→物理页转换结果及属性;
- MMU:硬件执行地址翻译和权限检查;TLB Miss时根据架构执行Page Walk,遇到不存在的映射或权限冲突时触发Page Fault等异常。
四、缺页异常Page Fault与按需分页Demand Paging
4.1 申请虚拟内存不等于立即分配全部物理页
一个常见误区是认为调用malloc后,请求大小对应的物理RAM就一定立即完成分配。实际上需要区分两种情况:
- 如果malloc能够直接复用用户态分配器中已有的空闲Chunk,这部分内存甚至不会进入内核;
- 如果用户态分配器需要通过brk扩展堆 或通过匿名mmap申请新的虚拟地址空间,在Linux典型的Demand Paging机制下,通常不会立即为整个虚拟地址范围分配并驻留全部物理数据页,而是在后续实际访问页面时通过Page Fault逐步建立物理页映射。
cpp
char* p = static_cast<char*>(malloc(100 * 1024 * 1024));
假设该大块请求最终由glibc通过匿名mmap向内核申请新的地址空间,那么此时主要建立的是虚拟地址映射关系;对应的数据页通常不会一次性全部驻留RAM,而是在后续实际访问时按需建立物理页映射。
4.2 第一次访问触发Page Fault
执行p[0] = 1;,CPU访问虚拟地址流程:
- CPU给出虚拟地址,MMU查询TLB,TLB Miss;
- 硬件遍历多级页表,发现PTE标记Not Present;
- MMU触发缺页异常,CPU陷入内核;
- 内核缺页处理函数,查找对应VMA,校验地址、访问权限合法性;
- 合法,则调用Page Allocator分配物理页,填充PTE建立映射;
- 返回用户态,重新执行触发异常的写指令。
4.3 完整访问链路示例
cpp
char* p = static_cast<char*>(malloc(100 * 1024 * 1024));
p[50 * 1024 * 1024] = 1;

五、物理内存管理:NUMA、Zone、PCP、Buddy
5.1 Page / Folio
Linux内核以Page为基础单元管理物理内存。x86_64默认基础页大小4KB。
内核通过struct page/Folio等数据结构维护页面相关元数据,例如引用计数、映射计数、页面状态Flag、Dirty状态、LRU/Active状态、锁状态以及文件映射或匿名内存相关信息。页面可能被多个地址空间共享(共享内存),page不一定是只属于一个进程的。
Linux近年来引入struct folio作为更高层的内存管理抽象,一个Folio可以包含一个或多个连续基础Page。它主要用于避免大量代码反复处理Compound Page的head/tail语义,并逐步成为Page Cache、匿名内存等MM路径中的重要管理单位。Folio是建立在Page之上的、更适合管理一组相关页面的抽象。
5.2 NUMA Node (Non-Uniform Memory Access)非一致内存访问
多 CPU 服务器架构,不同 CPU 访问内存的速度不一样
NUMA :内存被切分成多块,本地内存访问快,跨节点访问远端内存慢
多Socket服务器存在NUMA非一致内存架构:不同CPU访问本地内存延迟低,跨节点远端内存访问延迟显著更高。
text
CPU 0
│
└── NUMA Node 0
└── RAM 0
CPU 1
│
└── NUMA Node 1
└── RAM 1
内存分配时,内核需要选择分配节点,优先本地内存,减少远端访问延迟 ,对应内核numa_balancing自动页面迁移机制。
5.3 Zone内存域
单个NUMA Node内部,按硬件寻址限制划分Zone:ZONE_DMA、ZONE_DMA32、ZONE_NORMAL、ZONE_MOVABLE。
- ZONE_DMA / ZONE_DMA32 :专门存放物理低地址内存,用于寻址能力受限的传统 DMA 外设 。ZONE_DMA 对应 ISA 设备(x86 下 0~16MB);ZONE_DMA32 供只能访问 32 位物理地址(4GB 以内)的 DMA 控制器。现代设备优先使用 DMA 映射 API,不再强制依赖这两个 zone。
- ZONE_NORMAL :内核可直接线性映射的普通物理内存,既可以用于内核分配,也可用于用户态进程内存。64 位 x86 几乎全部内存都属于 ZONE_NORMAL。
- ZONE_MOVABLE :逻辑内存域,只接收可迁移类型页面(用户匿名页、pagecache 等) ;核心目的是支持内存热插拔(内存下线),同时辅助内存压缩 (compaction),降低物理碎片、便于分配大连续页(巨页 THP/HugePage)。内核不可迁移分配(kmalloc、页表等)不会从该 zone 分配。
物理内存层级关系:
text
Physical Memory
│
▼
NUMA Node
│
▼
Zone
│
▼
Page / Folio
5.4 Page Allocator分配路径
内核申请物理页入口alloc_pages(),分配优先级:
text
Page Allocation
│
▼
PCP Per-CPU Pageset
│
│PCP空闲页不足
▼
Buddy Allocator伙伴系统
5.5 PCP(Per-CPU Pageset)
多核场景,全局Buddy链表存在严重锁竞争。PCP为每个CPU维护本地页面缓存,单页分配优先从本CPUPCP取页,减少全局锁争抢。思想类似用户态tcache,本地缓存优先,不足再访问全局资源。
5.6 Buddy Allocator伙伴系统
负责连续物理页 分配与回收,分配单位2^order个连续Page。
一块 2k+12^{k+1}2k+1 大小的块对半分裂,得到两个 2k2^k2k 的子块,这一对就叫伙伴 (buddy) ;只有这一对伙伴同时空闲,才能合并回大块。
1. 基础数据结构
维护一组空闲链表数组 free_area \[\] ,数组下标叫 order:
free_area[order]:存放所有大小为2order2^{order}2order 个页的空闲连续块- order=0 → 20=12^0=120=1页(4KB)
- order=1 → 21=22^1=221=2页(8KB)
- order=n → 2n2^n2n页
块地址约束 :大小2k2^k2k的块,起始地址必须是2k2^k2k对齐。
伙伴地址计算公式 :块起始地址p,order=k,块大小S=2kS=2^kS=2k页:
buddy(p,k)={p+S(p mod 2S==0)p−S(p mod 2S==S)\text{buddy}(p,k)= \begin{cases} p+S & (p \bmod 2S ==0)\\ p-S & (p \bmod 2S ==S) \end{cases}buddy(p,k)={p+Sp−S(pmod2S==0)(pmod2S==S)
用位运算快速计算:buddy = p ^ (1 << k),这是 Linux 内核的写法。
2. 分配流程(allocate)
需求:申请n个连续页
- 向上取 order:找到最小的k,满足2k≥n2^k \ge n2k≥n
- 从
free_area[k]查找空闲块;- 有空闲块:取出标记为已占用,直接返回
- 无空闲块:向更大 order(k+1,k+2...)找空闲块
- 分裂 split:取出大块,切为一对伙伴。一块放回低 order 空闲链表,另一块继续递归分裂,直到得到 order=k 的块返回。
示例:申请 3 页,向上取 order=2(4 页),free_area 2 为空,则去 order=3(8 页)取块,分裂成两个 4 页。一个 4 页分配出去,另一个放入 free_area 2。
3. 释放流程(free)
释放起始地址p,order=k 的块:
- 将块标记空闲,插入
free_area[k] - 计算它的伙伴地址,检查伙伴是否空闲
- 伙伴空闲:两个块从链表移除,合并成2k+12^{k+1}2k+1大块;k += 1,回到步骤 2递归向上合并
- 伙伴已占用:停止合并,释放结束
释放时自动合并,不需要单独内存整理,这是伙伴系统最大优势。
4. 优缺点
优点
- 合并 / 分裂快,O(logM)O(\log M)O(logM),M 是最大 order;位运算找伙伴,开销极低
- 大幅减少外部碎片;释放时自动合并相邻同大小空闲块
- 容易快速分配连续物理内存(DMA、大页、帧缓存场景)
缺点
- 内部碎片严重:申请大小不是 2 的幂,必须向上取整。例如申请 5 页,分配 8 页,3 页浪费;最坏浪费接近 50%
- 只能合并严格的伙伴块;普通相邻但不是同源分裂的空闲块不能合并(这是二进制伙伴的局限)
分配时:大块内存递归拆分;释放时,检查相邻伙伴页是否空闲,满足条件则向上合并。核心逻辑Split与Merge,用于管理物理内存碎片,满足DMA、THP透明大页对连续物理内存的需求。
六、内核分配器与用户态分配器
6.1 Buddy为什么不能直接malloc(24)
Buddy最小粒度是Page,直接分配会产生巨大内部碎片。Buddy只管理整页,小对象需要SLUB分配器,将Page切分成更小内核对象。
6.2 SLAB / SLUB分配器
Linux现代内核默认使用SLUB。伙伴系统 (buddy) 分配整页物理内存,slab 在整页之上做对象级内存分配 ,专门解决频繁创建销毁固定大小内核对象 的场景,用于task_struct、inode、dentry、file、socket等内核小对象。
#mermaid-svg-B0r6MmzvAE88JlaK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-B0r6MmzvAE88JlaK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B0r6MmzvAE88JlaK .error-icon{fill:#552222;}#mermaid-svg-B0r6MmzvAE88JlaK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B0r6MmzvAE88JlaK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B0r6MmzvAE88JlaK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B0r6MmzvAE88JlaK .marker.cross{stroke:#333333;}#mermaid-svg-B0r6MmzvAE88JlaK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B0r6MmzvAE88JlaK p{margin:0;}#mermaid-svg-B0r6MmzvAE88JlaK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B0r6MmzvAE88JlaK .cluster-label text{fill:#333;}#mermaid-svg-B0r6MmzvAE88JlaK .cluster-label span{color:#333;}#mermaid-svg-B0r6MmzvAE88JlaK .cluster-label span p{background-color:transparent;}#mermaid-svg-B0r6MmzvAE88JlaK .label text,#mermaid-svg-B0r6MmzvAE88JlaK span{fill:#333;color:#333;}#mermaid-svg-B0r6MmzvAE88JlaK .node rect,#mermaid-svg-B0r6MmzvAE88JlaK .node circle,#mermaid-svg-B0r6MmzvAE88JlaK .node ellipse,#mermaid-svg-B0r6MmzvAE88JlaK .node polygon,#mermaid-svg-B0r6MmzvAE88JlaK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B0r6MmzvAE88JlaK .rough-node .label text,#mermaid-svg-B0r6MmzvAE88JlaK .node .label text,#mermaid-svg-B0r6MmzvAE88JlaK .image-shape .label,#mermaid-svg-B0r6MmzvAE88JlaK .icon-shape .label{text-anchor:middle;}#mermaid-svg-B0r6MmzvAE88JlaK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B0r6MmzvAE88JlaK .rough-node .label,#mermaid-svg-B0r6MmzvAE88JlaK .node .label,#mermaid-svg-B0r6MmzvAE88JlaK .image-shape .label,#mermaid-svg-B0r6MmzvAE88JlaK .icon-shape .label{text-align:center;}#mermaid-svg-B0r6MmzvAE88JlaK .node.clickable{cursor:pointer;}#mermaid-svg-B0r6MmzvAE88JlaK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B0r6MmzvAE88JlaK .arrowheadPath{fill:#333333;}#mermaid-svg-B0r6MmzvAE88JlaK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B0r6MmzvAE88JlaK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B0r6MmzvAE88JlaK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B0r6MmzvAE88JlaK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B0r6MmzvAE88JlaK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B0r6MmzvAE88JlaK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B0r6MmzvAE88JlaK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B0r6MmzvAE88JlaK .cluster text{fill:#333;}#mermaid-svg-B0r6MmzvAE88JlaK .cluster span{color:#333;}#mermaid-svg-B0r6MmzvAE88JlaK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-B0r6MmzvAE88JlaK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B0r6MmzvAE88JlaK rect.text{fill:none;stroke-width:0;}#mermaid-svg-B0r6MmzvAE88JlaK .icon-shape,#mermaid-svg-B0r6MmzvAE88JlaK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B0r6MmzvAE88JlaK .icon-shape p,#mermaid-svg-B0r6MmzvAE88JlaK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B0r6MmzvAE88JlaK .icon-shape .label rect,#mermaid-svg-B0r6MmzvAE88JlaK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B0r6MmzvAE88JlaK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B0r6MmzvAE88JlaK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B0r6MmzvAE88JlaK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-B0r6MmzvAE88JlaK .buddy>*{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-B0r6MmzvAE88JlaK .buddy span{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-B0r6MmzvAE88JlaK .buddy tspan{fill:#0d47a1!important;}#mermaid-svg-B0r6MmzvAE88JlaK .slub>*{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-B0r6MmzvAE88JlaK .slub span{fill:#fff8e1!important;stroke:#fb8c00!important;stroke-width:1.5px!important;color:#e65100!important;}#mermaid-svg-B0r6MmzvAE88JlaK .slub tspan{fill:#e65100!important;}#mermaid-svg-B0r6MmzvAE88JlaK .obj>*{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-B0r6MmzvAE88JlaK .obj span{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-B0r6MmzvAE88JlaK .obj tspan{fill:#1b5e20!important;} Pages
Buddy Allocator
SLUB
Object
Object
Object
Object
概念
- kmem_cache(缓存描述符)
每一类内核对象对应一个kmem_cache,比如task_struct缓存、inode 缓存。
里面记录:对象大小、对齐、每个 slab 包含多少对象、构造 / 析构函数。 - Slab
从 buddy 拿到的一个或多个连续物理页 ,把这一整块页切分成 N 个等大对象 。
一个 slab 有 3 种状态:
满(full):所有对象都被分配出去部分(partial):一部分对象在用,一部分空闲(优先从 partial 分配)空(free):全部对象空闲,可归还 buddy 伙伴系统
- 对象 (object):slab 里面最小单元,就是要分配的内核结构体实例。
- slab 的空闲标记
早期 slab:用freelist数组记录 slab 内空闲对象索引;
现代 SLUB(Linux 默认)简化:不再单独维护 freelist 数组,把下一个空闲对象指针直接存在空闲对象本身内存里。
分配流程 kmem_cache_alloc
- 在当前
kmem_cache找 partial slab(有空闲对象的 slab) - 从 partial slab 取出一个空闲对象,标记为已占用,返回对象指针
- 如果没有 partial slab:新建 slab
- 调用 buddy 系统,申请若干连续物理页
- 将页面切分成多个对象,可选执行对象构造 ctor初始化
- 放入该 cache 的 partial 链表,再分配对象
- 分配失败:尝试内存回收(页面回收),失败返回 NULL
释放流程 kmem_cache_free
- 将对象标记回空闲,写回 freelist
- 所属 slab 状态:
- 如果 slab 从「部分」变成完全空闲
- 当系统内存紧张:把整个 slab 页面释放回 buddy 伙伴系统
- 内存充裕:保留 slab,缓存对象,下次直接复用,减少 buddy 调用
- 如果 slab 从「部分」变成完全空闲
重点:释放对象不会把页面立刻还给 buddy,slab 缓存页保留。
kmalloc(128, GFP_KERNEL)优先从SLUB缓存获取对象,SLUB缓存缺页时,才向下调用Page Allocator向Buddy申请物理页。
6.3 alloc_pages / kmalloc / vmalloc
alloc_pages(gfp, order):直接调用Page Allocator,分配2^order物理连续页面;kmalloc():内核小块内存,虚拟连续,物理连续 ,底层SLUB+Buddy;分配标记GFP_KERNEL允许休眠,GFP_ATOMIC禁止休眠,用于中断上下文;vmalloc():虚拟地址连续,物理地址不要求连续。多个分散物理页,通过页表映射为一段连续内核虚拟地址。适合大块内核缓冲区,代价是TLB开销更高。
6.4 glibc malloc(用户态分配器)
malloc不是系统调用 ,是glibc实现的用户态库函数 ,ptmalloc2为现代glibc默认实现。
用户态分配器不会每次分配都陷入内核,会提前向内核申请大块虚拟内存,在用户态切分Chunk自行管理。
text
Kernel
│大块虚拟内存brk/mmap
▼
glibc allocator
text
Heap
┌─────────┬───────────┬────────────┬──────────────┐
│ Chunk A │ Chunk B │ Chunk C │ Free Chunk │
└─────────┴───────────┴────────────┴──────────────┘
Arena
Arena:分配区 ,是 glibc ptmalloc2 为了解决多线程 malloc 全局锁竞争而设计的独立内存管理实例,对应结构体 struct malloc_state。
早期 malloc 全局一把锁,多线程频繁 malloc/free 会严重串行阻塞;
每个 Arena 独立带锁,多个线程可以并行在不同 Arena 分配内存,降低锁竞争。
两类 Arena
- main_arena(主分配区)
- 进程启动自动创建,主线程使用
- 可以用 sbrk 扩展堆,也能用 mmap
- 全局唯一,不在堆上,存在 libc 的静态数据段
- non-main arena(非主分配区 /sub-arena)
- 子线程按需创建,只能用 mmap 向系统申请内存(每次 64MB)
- 每个 arena 自带一把独立 mutex 锁,多个 arena 连成单向循环链表
Arena 数量上限
- 32 位:
2 * CPU核心数 + 1 - 64 位:
8 * CPU核心数
#mermaid-svg-5FRvMwTBBniwUuxV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5FRvMwTBBniwUuxV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5FRvMwTBBniwUuxV .error-icon{fill:#552222;}#mermaid-svg-5FRvMwTBBniwUuxV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5FRvMwTBBniwUuxV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5FRvMwTBBniwUuxV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5FRvMwTBBniwUuxV .marker.cross{stroke:#333333;}#mermaid-svg-5FRvMwTBBniwUuxV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5FRvMwTBBniwUuxV p{margin:0;}#mermaid-svg-5FRvMwTBBniwUuxV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5FRvMwTBBniwUuxV .cluster-label text{fill:#333;}#mermaid-svg-5FRvMwTBBniwUuxV .cluster-label span{color:#333;}#mermaid-svg-5FRvMwTBBniwUuxV .cluster-label span p{background-color:transparent;}#mermaid-svg-5FRvMwTBBniwUuxV .label text,#mermaid-svg-5FRvMwTBBniwUuxV span{fill:#333;color:#333;}#mermaid-svg-5FRvMwTBBniwUuxV .node rect,#mermaid-svg-5FRvMwTBBniwUuxV .node circle,#mermaid-svg-5FRvMwTBBniwUuxV .node ellipse,#mermaid-svg-5FRvMwTBBniwUuxV .node polygon,#mermaid-svg-5FRvMwTBBniwUuxV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5FRvMwTBBniwUuxV .rough-node .label text,#mermaid-svg-5FRvMwTBBniwUuxV .node .label text,#mermaid-svg-5FRvMwTBBniwUuxV .image-shape .label,#mermaid-svg-5FRvMwTBBniwUuxV .icon-shape .label{text-anchor:middle;}#mermaid-svg-5FRvMwTBBniwUuxV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5FRvMwTBBniwUuxV .rough-node .label,#mermaid-svg-5FRvMwTBBniwUuxV .node .label,#mermaid-svg-5FRvMwTBBniwUuxV .image-shape .label,#mermaid-svg-5FRvMwTBBniwUuxV .icon-shape .label{text-align:center;}#mermaid-svg-5FRvMwTBBniwUuxV .node.clickable{cursor:pointer;}#mermaid-svg-5FRvMwTBBniwUuxV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5FRvMwTBBniwUuxV .arrowheadPath{fill:#333333;}#mermaid-svg-5FRvMwTBBniwUuxV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5FRvMwTBBniwUuxV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5FRvMwTBBniwUuxV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5FRvMwTBBniwUuxV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5FRvMwTBBniwUuxV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5FRvMwTBBniwUuxV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5FRvMwTBBniwUuxV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5FRvMwTBBniwUuxV .cluster text{fill:#333;}#mermaid-svg-5FRvMwTBBniwUuxV .cluster span{color:#333;}#mermaid-svg-5FRvMwTBBniwUuxV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5FRvMwTBBniwUuxV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5FRvMwTBBniwUuxV rect.text{fill:none;stroke-width:0;}#mermaid-svg-5FRvMwTBBniwUuxV .icon-shape,#mermaid-svg-5FRvMwTBBniwUuxV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5FRvMwTBBniwUuxV .icon-shape p,#mermaid-svg-5FRvMwTBBniwUuxV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5FRvMwTBBniwUuxV .icon-shape .label rect,#mermaid-svg-5FRvMwTBBniwUuxV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5FRvMwTBBniwUuxV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5FRvMwTBBniwUuxV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5FRvMwTBBniwUuxV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-5FRvMwTBBniwUuxV .startEnd>*{fill:#E8F5E9!important;stroke:#2E7D32!important;stroke-width:2px!important;color:#1B5E20!important;}#mermaid-svg-5FRvMwTBBniwUuxV .startEnd span{fill:#E8F5E9!important;stroke:#2E7D32!important;stroke-width:2px!important;color:#1B5E20!important;}#mermaid-svg-5FRvMwTBBniwUuxV .startEnd tspan{fill:#1B5E20!important;}#mermaid-svg-5FRvMwTBBniwUuxV .process>*{fill:#E3F2FD!important;stroke:#1976D2!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-5FRvMwTBBniwUuxV .process span{fill:#E3F2FD!important;stroke:#1976D2!important;stroke-width:1.5px!important;color:#0D47A1!important;}#mermaid-svg-5FRvMwTBBniwUuxV .process tspan{fill:#0D47A1!important;}#mermaid-svg-5FRvMwTBBniwUuxV .decision>*{fill:#FFF8E1!important;stroke:#F9A825!important;stroke-width:1.5px!important;color:#795548!important;}#mermaid-svg-5FRvMwTBBniwUuxV .decision span{fill:#FFF8E1!important;stroke:#F9A825!important;stroke-width:1.5px!important;color:#795548!important;}#mermaid-svg-5FRvMwTBBniwUuxV .decision tspan{fill:#795548!important;}#mermaid-svg-5FRvMwTBBniwUuxV .action>*{fill:#F3E5F5!important;stroke:#8E24AA!important;stroke-width:1.5px!important;color:#4A148C!important;}#mermaid-svg-5FRvMwTBBniwUuxV .action span{fill:#F3E5F5!important;stroke:#8E24AA!important;stroke-width:1.5px!important;color:#4A148C!important;}#mermaid-svg-5FRvMwTBBniwUuxV .action tspan{fill:#4A148C!important;}#mermaid-svg-5FRvMwTBBniwUuxV .retry>*{fill:#FFEBEE!important;stroke:#C62828!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-5FRvMwTBBniwUuxV .retry span{fill:#FFEBEE!important;stroke:#C62828!important;stroke-width:1.5px!important;color:#B71C1C!important;}#mermaid-svg-5FRvMwTBBniwUuxV .retry tspan{fill:#B71C1C!important;} 成功
失败
有,取下一个
没有,全部失败
未达上限
已达上限
否
是
线程调用 malloc
遍历 arena 链表
尝试 lock 当前 arena
使用该 arena 分配
结束
还有下一个 arena?
arena 总数达上限?
新建 non-main arena
lock 新 arena
循环重试抢现有 arena 锁
抢到锁?
Arena 内部组成(层级)
Arena(malloc_state)
├─ mutex:arena锁
├─ top chunk:arena的剩余连续堆顶
├─ fastbins:快速释放小块链表
├─ bins:unsorted bin + small bins + large bins
└─ heap:一个arena可以包含多个heap(mmap出来的heap segment)
Chunk:arena 管理的最小内存单元,用户 malloc 拿到的就是 chunk 的可用区域。
tcache(Thread Cache)
tcache(Thread Local Cache,线程本地缓存) 每个线程独立一份,无锁,专门加速小块内存 malloc/free,放在 Arena 上层。
Arena 是多线程共享、带锁;tcache 是线程私有、完全不用锁 。小对象malloc优先从tcache取空闲chunk,tcache缺失才进入Arena、争抢Arena锁;Arena空闲不足,才触发brk/mmap系统调用进入内核。
tcache -> Arena -> Kernel和PCP -> Buddy架构思想都是:局部缓存降低全局锁压力。
6.5 free() 后RSS不一定下降
free调用,仅标记Chunk为空闲,归还到tcache/bin/arena,不代表物理内存立刻还给内核。内核RSS(Resident Set Size,驻留物理内存)不会马上降低。
free后RSS不下降 ≠ 内存泄漏,是用户态分配器缓存策略。
6.6 大块分配:匿名mmap
当分配尺寸超过阈值,glibc使用mmap匿名映射,独立VMA。释放调用munmap,直接将虚拟区域归还内核,页面更容易回收。
6.7 brk / sbrk
brk 是内核系统调用(绝对地址);sbrk 是 glibc 在用户态封装的库函数(相对偏移),底层还是调用 brk ,用来移动 program break(程序断点),也就是堆的上边界水位线
- 堆是一块连续虚拟地址区域,在数据段上方;向上增长。
- 只有 main_arena(主分配区)使用 sbrk 扩展堆;non-main arena(子线程 arena)只能用
mmap创建独立 64MB sub-heap,不能用 sbrk。 - 限制:只能回收堆最顶部连续空闲内存 。中间的空闲 chunk,哪怕很大,也没法通过 sbrk 还给内核,只能留在 ptmalloc 池里复用(内存碎片根源)。
- 只有 top chunk 空闲大小超过阈值(默认 128KB),
malloc_trim或者 free 时才会调用 sbrk 收缩堆、归还内存给内核。
- 只有 top chunk 空闲大小超过阈值(默认 128KB),
传统堆使用brk系统调用,修改program break指针,向上扩展堆VMA。现代应用不建议直接调用brk,统一封装在malloc接口下。
malloc
├─ 优先走线程私有tcache(无锁)
├─ tcache空 → 找arena分配
│ ├─ arena内部空闲chunk不够,需要向内核申请内存
│ │ ├─ main_arena:调用sbrk抬高program break,连续扩展堆
│ │ └─ non-main arena:mmap新建sub-heap(64MB)
└─ 超大分配(>M_MMAP_THRESHOLD 默认128KB):直接mmap匿名映射,free时munmap直接还给内核,不走任何bin/tcache
6.8 malloc(100)三种场景
- tcache命中:直接返回空闲chunk,0次系统调用;
- tcache miss,Arena有空闲chunk:切分chunk返回,不进入内核;
- Arena耗尽:调用brk/mmap,内核新建VMA。此时只分配虚拟地址,物理页仍然延迟到第一次访问触发缺页分配。
七、内存压力、页面回收、Compaction、OOM
7.1 Page Reclaim页面回收
内存不足时,Linux不会直接返回分配失败,尝试回收可回收页面。页面分为可回收与不可回收:
- 可回收:Page Cache文件缓存、可Swap匿名内存;
- 不可回收:内核锁定页面、内核代码、mlock锁定用户内存。
7.2 Page Cache
Linux利用空闲内存缓存文件IO。读文件时,磁盘内容加载到Page Cache。Clean Page Cache,磁盘存在副本,内存压力下可直接丢弃;Dirty Page Cache需要先回写磁盘,再回收页面。Page Cache是Linux提升文件IO吞吐的核心机制。
7.3 Anonymous匿名内存 & Swap
malloc拿到的匿名内存(anonymous memory) :没有关联磁盘上的普通文件,物理内存不够的时候,内核可以把这部分内存页写到 Swap(交换分区 / 交换文件)腾物理内存;之后程序再访问这一页,CPU 缺页异常,内核做SwapIn,把 Swap 上的数据读回物理内存,程序继续运行。
1. 匿名页(匿名内存)
- 匿名:不绑定任何磁盘文件 。
mmap(MAP_ANONYMOUS)、malloc(底层 brk/mmap)分配出来的堆内存就是匿名页。 - 对比:
mmap映射一个磁盘文件得到的是文件映射页,它的后端是磁盘文件,内存紧张时可以直接丢弃,需要时从原文件读;匿名页没有原文件,所以只能写到 Swap 保存数据。
malloc 不是直接分配物理内存:只是在进程虚拟地址空间 里划出一块地址。第一次读写的时候才触发缺页,内核分配物理页框(延迟分配,写时复制 COW 配套机制)。
2. 内存紧张 → 匿名页换出(Swap Out)
物理内存不够用的时候,内核的页回收逻辑会挑选一些冷页(很久没访问):
- 对文件页:脏页写回原文件、干净页直接丢掉;
- 对匿名页 :没有原始文件可以回写,内核只能把页内容拷贝到 Swap 分区 /swapfile,然后释放这块物理内存。
- 页表项会被标记:这一页不在内存,数据在 Swap 的某个位置。
这一步叫 SwapOut(换出)。换出之后,进程的虚拟地址还在,但对应的物理内存已经被收走。
3. 再次访问该页 → 缺页异常 → SwapIn(换入)
进程读写这个虚拟地址:
- MMU 查页表,发现有效位为 0,触发缺页异常(page fault);
- 内核进入缺页处理函数,判断这是匿名页,并且已经被换到 Swap;
- 执行 SwapIn:从磁盘 Swap 位置读取页面数据,分配新物理页框,把数据加载进去;
- 更新页表,指向新物理页;
- 返回用户态,重新执行触发异常的那条指令,程序无感继续运行。
- Swap 不是必须的:系统可以无 Swap。此时内存紧张,匿名页不能换出,内核会触发 OOM,杀掉进程释放内存。
- 换入换出代价极高:磁盘 IO,比内存慢上万倍。大量 SwapIn/SwapOut 就是常说的抖动(thrashing),系统卡死。
- 不是所有匿名页都会被换出:可以用
mlock锁定内存,被锁定的匿名页永远留在物理内存,不会被 SwapOut。- 写时复制 fork:子进程的匿名页一开始和父进程共享物理页;写的时候才拷贝。拷贝出来的新页依然是匿名页,同样可以被换入换出。
7.4 kswapd后台回收线程
Linux 把同一个 NUMA 节点内的物理内存,按硬件访问限制划分成若干连续物理地址区间,每个区间就是一个Zone(内存域) 。伙伴系统、页面回收、kswapd、Swap 换页,全部是以 Zone 为单位管理
每个Zone维护三个水位线:High / Low / Min。
- high:空闲页充足,kswapd 休眠,不做回收
- low:空闲页低于 low → 唤醒 kswapd 后台回收(扫描 LRU,匿名页 SwapOut、文件页回收)
- min :低于 min,kswapd 来不及,直接同步回收;再失败触发 OOM Killer
text
Free Memory
│
▼
低于Low Watermark
│
▼
唤醒kswapd内核线程,后台异步回收页面
│
▼
直到空闲内存回升至High Watermark,kswapd休眠
kswapd是后台守护线程,目标提前回收内存,尽量避免分配路径直接阻塞。kswapd何时运行,由CPU调度器决定。
7.5 Direct Reclaim直接回收
Zone 空闲内存跌破 watermark[min],kswapd 后台回收已经跟不上 。此时发起内存分配的当前线程,不再等待 kswapd,自己在 alloc_pages 分配路径里同步执行页面回收 ,这就是 Direct Reclaim(直接回收)。
线程原地阻塞,一边回收页面一边尝试凑出空闲页;回收过程会带来巨大 IO 抖动(Jitter),是业务延迟尖峰的重要来源。
空闲 < low:唤醒 kswapd 内核线程,后台异步回收,分配线程照常走分配,不阻塞。kswapd 在后台扫 LRU、换出匿名页、回写脏文件页,把空闲内存拉回 high 水位,之后 kswapd 休眠。空闲 < min:kswapd 来不及释放足够页,分配线程进入 Direct Reclaim。分配线程自己执行回收逻辑,线程被阻塞,直到回收释放出足够页面或者回收失败。
Direct Reclaim 完整流程
alloc_pages进入,检查目标 Zone 水位,发现空闲页 < min;- 进入直接回收路径
__alloc_pages_direct_reclaim; - 调用
shrink_zones→shrink_lruvec,扫描当前 Zone 的 LRU 链表:- 文件干净页:直接释放
- 文件脏页:触发回写磁盘
- 匿名页:必须 SwapOut,把页写入 Swap 磁盘(这一步最耗时,磁盘 IO)
- 每回收一批页面,再次尝试
alloc_pages;- 成功拿到页:分配返回,线程继续运行;
- 反复回收仍然凑不出内存:直接回收失败,进入 OOM Killer。
7.6 实时系统视角:内存分配带来的抖动
- kswapd 是后台内核线程,就算有 SwapOut IO,只会占用后台资源,用户业务线程不受直接阻塞;
- Direct Reclaim 是用户进程 / 内核线程原地等待 IO:业务代码执行到 malloc,卡在分配路径,等待 Swap 写盘;Swap IO 耗时毫秒甚至几十 ms,业务延迟瞬间飙升,出现延迟尖峰。
机器人视觉、控制等高实时场景,运行时new/malloc/vector::resize会引入不确定延迟尖峰:
text
malloc
↓
Allocator Slow Path
↓
内核分配
↓
Page Fault
↓
Direct Reclaim
↓
线程休眠唤醒
↓
Latency Spike → Jitter
工程解决方案:内存池、对象池、预分配Buffer、mlock锁定内存,运行时不再动态分配,消除缺页与回收抖动。
7.7 两层内存碎片
- 用户态分配器碎片:总空闲内存足够,但没有足够大连续Chunk。例如空闲96B,申请80B,碎片导致分配失败;
- 物理内存碎片(Buddy碎片) :大量离散空闲Page,但是没有连续物理Page。即使总空闲页足够,无法满足大块连续物理内存请求(DMA、THP)。
7.8 Memory Compaction内存规整
为解决物理碎片,Linux执行内存规整:迁移页面,把占用页面压缩到Zone一端,空闲页合并为大块连续物理内存。
Before: U F U F U F U F
After: U U U U F F F F
规整分为异步后台规整、同步规整(分配请求路径触发)。
现象:系统free内存很高,但是无法分配大段连续物理页,大概率物理碎片。
7.9 OOM Killer
当内存分配长期无法取得进展时,内核在满足相应条件的情况下可能进入OOM处理流程,并根据OOM评分等因素选择进程终止以释放内存。
但并非所有页面分配失败都会触发OOM Killer:分配行为还受到GFP Flags、分配Order、Memcg限制以及调用上下文等因素影响,例如部分High-order分配或禁止重试的分配可能直接失败并返回NULL,而不会启动OOM Killer。
7.10 Overcommit内存超分配:malloc成功不等于物理内存已经准备好
Linux支持Memory Overcommit,但是否接受一个远大于实际RAM+Swap的虚拟内存申请,取决于vm.overcommit_memory策略:
- 0:启发式Overcommit,也是Linux常见默认模式;允许一定程度的超分配,但明显不合理的大型申请仍可能被拒绝;
- 1:Always Overcommit,内核尽量允许虚拟地址空间申请,直到后续真正消耗内存时出现压力;
- 2:严格Commit Accounting,根据Swap和配置的Commit Limit限制可提交内存。
因此,malloc(100GB)即使机器没有100GB可用物理内存,有可能返回成功,但绝不能认为一定成功。即使malloc已经返回非空指针,也只代表当前虚拟内存申请被分配器和内核接受,并不意味着对应大小的物理RAM已经全部准备完成。
后续程序真正写入这些页面时,会逐步建立物理页映射;如果持续产生的实际内存需求超过系统能够承受的范围,则可能进入页面回收、Swap,最终在满足OOM条件时触发OOM处理。
malloc返回成功 ≠ 对应大小的物理内存已经分配并得到绝对保证。
八、内存管理子系统与CPU调度器协作
- CPU调度器:管理就绪线程队列,决定线程调度、切换、休眠唤醒;
- MM子系统:内存映射、缺页处理、页面回收;
二者典型协作场景:线程访问未驻留内存,触发Page Fault,需要磁盘IO加载页面:
#mermaid-svg-kA3Hl8gFZreWZb6A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kA3Hl8gFZreWZb6A .error-icon{fill:#552222;}#mermaid-svg-kA3Hl8gFZreWZb6A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kA3Hl8gFZreWZb6A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kA3Hl8gFZreWZb6A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kA3Hl8gFZreWZb6A .marker.cross{stroke:#333333;}#mermaid-svg-kA3Hl8gFZreWZb6A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kA3Hl8gFZreWZb6A p{margin:0;}#mermaid-svg-kA3Hl8gFZreWZb6A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A .cluster-label text{fill:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A .cluster-label span{color:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A .cluster-label span p{background-color:transparent;}#mermaid-svg-kA3Hl8gFZreWZb6A .label text,#mermaid-svg-kA3Hl8gFZreWZb6A span{fill:#333;color:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A .node rect,#mermaid-svg-kA3Hl8gFZreWZb6A .node circle,#mermaid-svg-kA3Hl8gFZreWZb6A .node ellipse,#mermaid-svg-kA3Hl8gFZreWZb6A .node polygon,#mermaid-svg-kA3Hl8gFZreWZb6A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kA3Hl8gFZreWZb6A .rough-node .label text,#mermaid-svg-kA3Hl8gFZreWZb6A .node .label text,#mermaid-svg-kA3Hl8gFZreWZb6A .image-shape .label,#mermaid-svg-kA3Hl8gFZreWZb6A .icon-shape .label{text-anchor:middle;}#mermaid-svg-kA3Hl8gFZreWZb6A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kA3Hl8gFZreWZb6A .rough-node .label,#mermaid-svg-kA3Hl8gFZreWZb6A .node .label,#mermaid-svg-kA3Hl8gFZreWZb6A .image-shape .label,#mermaid-svg-kA3Hl8gFZreWZb6A .icon-shape .label{text-align:center;}#mermaid-svg-kA3Hl8gFZreWZb6A .node.clickable{cursor:pointer;}#mermaid-svg-kA3Hl8gFZreWZb6A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kA3Hl8gFZreWZb6A .arrowheadPath{fill:#333333;}#mermaid-svg-kA3Hl8gFZreWZb6A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kA3Hl8gFZreWZb6A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kA3Hl8gFZreWZb6A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kA3Hl8gFZreWZb6A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kA3Hl8gFZreWZb6A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kA3Hl8gFZreWZb6A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kA3Hl8gFZreWZb6A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kA3Hl8gFZreWZb6A .cluster text{fill:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A .cluster span{color:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kA3Hl8gFZreWZb6A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kA3Hl8gFZreWZb6A rect.text{fill:none;stroke-width:0;}#mermaid-svg-kA3Hl8gFZreWZb6A .icon-shape,#mermaid-svg-kA3Hl8gFZreWZb6A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kA3Hl8gFZreWZb6A .icon-shape p,#mermaid-svg-kA3Hl8gFZreWZb6A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kA3Hl8gFZreWZb6A .icon-shape .label rect,#mermaid-svg-kA3Hl8gFZreWZb6A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kA3Hl8gFZreWZb6A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kA3Hl8gFZreWZb6A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kA3Hl8gFZreWZb6A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-kA3Hl8gFZreWZb6A .mm>*{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-kA3Hl8gFZreWZb6A .mm span{fill:#e3f2fd!important;stroke:#1e88e5!important;stroke-width:1.5px!important;color:#0d47a1!important;}#mermaid-svg-kA3Hl8gFZreWZb6A .mm tspan{fill:#0d47a1!important;}#mermaid-svg-kA3Hl8gFZreWZb6A .cpu>*{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-kA3Hl8gFZreWZb6A .cpu span{fill:#e8f5e9!important;stroke:#43a047!important;stroke-width:1.5px!important;color:#1b5e20!important;}#mermaid-svg-kA3Hl8gFZreWZb6A .cpu tspan{fill:#1b5e20!important;} CPU 调度器
内存管理子系统
触发缺页
线程 A 阻塞
等待 IO 完成
就绪
Page Fault
需要磁盘 IO
处理缺页
IO 完成
唤醒 Thread A
加入就绪队列
Thread A Running
线程 A 阻塞
进入睡眠状态
Scheduler 切换 CPU
执行 Thread B
Scheduler 决定
Thread A 重新获得 CPU
kswapd是内核线程,MM子系统负责唤醒kswapd变为就绪态;kswapd什么时候执行,由CPU调度器决定。
工程提到的内存调度、NUMA调度,本质是页面放置、页面迁移、回收策略,属于MM子系统策略,不是调度器。
总结
text
Linux Memory Management
│
├── Process Virtual Memory
│ ├── mm_struct
│ ├── VMA
│ ├── mmap
│ ├── brk
│ └── Memory Permission
│
├── Address Translation
│ ├── Page Table
│ │ ├── PGD
│ │ ├── P4D
│ │ ├── PUD
│ │ ├── PMD
│ │ └── PTE
│ │
│ ├── MMU
│ ├── TLB
│ └── Page Walk
│
├── Page Fault
│ ├── Demand Paging
│ ├── Anonymous Page
│ ├── File-backed Page
│ └── Copy-on-Write(fork核心机制)
│
├── Physical Memory
│ ├── NUMA Node
│ ├── Zone
│ ├── Page / Folio
│ ├── PCP
│ └── Buddy Allocator
│
├── Kernel Allocation
│ ├── alloc_pages
│ ├── kmalloc
│ ├── SLAB / SLUB
│ └── vmalloc
│
├── Userspace Allocator
│ ├── glibc malloc ptmalloc
│ ├── Chunk
│ ├── tcache
│ ├── Arena
│ ├── Bins
│ ├── brk
│ └── mmap
│
└── Memory Pressure
├── Page Cache
├── Page Reclaim
├── kswapd
├── Direct Reclaim
├── Swap
├── Memory Compaction
└── OOM Killer