内存申请(Memory Allocation)的本质是向操作系统或运行时请求一块连续的虚拟地址空间,并将其映射到可用的物理内存(或交换空间)。下面从底层到上层,分层说明其原理:


一、两种基本申请路径

类型 管理方式 生命周期 典型场景
栈内存(Stack) 编译器自动管理,CPU通过SP/BP寄存器维护 随函数调用分配,返回即释放 局部变量、函数参数
堆内存(Heap) 程序员/垃圾回收器手动/自动管理 从申请到显式释放或进程结束 mallocnew、动态数据结构

二、堆内存申请的系统调用层

程序调用 malloc 时,并不会每次都直接找操作系统要内存,而是分两级:

1. 向操作系统"批发"内存(系统调用)

Linux 下主要有两个系统调用:

  • brk():调整程序数据段的结束地址(Break),向上扩展堆顶。适合申请小块连续内存(通常 < 128KB,阈值可配置)。
  • mmap():在虚拟地址空间中单独映射一块匿名页。适合申请大块内存(>= 128KB),或需要独立释放的大对象。

Windows 对应的是 VirtualAlloc,机制类似。

2. 用户态内存分配器"零售"(glibc ptmalloc / jemalloc / tcmalloc)

操作系统以页(通常 4KB)为单位分配,但程序可能只需要 24 字节。因此 C 库的 malloc 实现(如 ptmalloc)会维护复杂的空闲块管理结构

  • 空闲链表(Bins):将释放的内存块按大小分类缓存,下次申请时优先复用。
  • 切割与合并:大块切小分配;相邻空闲块合并,减少碎片。
  • 线程缓存(Thread Cache):每个线程先用自己的缓存池拿内存,避免全局锁竞争。

三、虚拟内存映射机制

操作系统返回给程序的地址都是虚拟地址,需要通过 MMU(内存管理单元) 映射到物理内存:

虚拟地址空间
├─ 已分配页 ──→ 页表 ──→ 物理页框(RAM)
├─ 已分配页 ──→ 页表 ──→ 未映射(首次访问触发缺页中断,才分配物理页)
└─ 未分配页 ──→ 访问时触发 Segmentation Fault

关键优化:延迟分配(Demand Paging)

  • malloc/mmap 申请时,内核往往只记录"这块虚拟地址归你了",并不立即分配物理页。
  • 直到真正读写该内存时,触发缺页中断(Page Fault),内核才分配物理页框并建立映射。
  • 这使得申请大内存的成本很低,物理内存只在实际使用时消耗。

四、内存碎片问题

碎片类型 原因 后果
内部碎片 分配粒度对齐(如只要 10 字节,给了 16 字节) 已分配块内部浪费
外部碎片 频繁申请/释放不同大小的块,导致空闲块不连续 总空闲内存够,但无法满足大块连续申请

现代分配器通过伙伴系统(Buddy System)slab 分配器地址排序合并等算法来缓解碎片。


五、一个完整的 malloc 流程示例

void *p = malloc(100);
  1. 判断大小:100 字节 < 阈值(如 128KB)→ 走堆(brk 区域)。
  2. 查找空闲块:在 ptmalloc 的 fast bin / small bin 中查找是否有合适大小的空闲块。
  3. 命中缓存:找到一个 128 字节的空闲块,切出 100 字节给用户,剩余 28 字节放回空闲链表。
  4. 未命中缓存:分配器发现堆顶(brk)不够,执行 brk() 向内核申请扩展几页内存。
  5. 内核处理brk 系统调用修改进程 VMA(虚拟内存区域),标记新的虚拟地址范围有效,但不分配物理页。
  6. 用户访问:程序写入 p[0] → 缺页中断 → 内核分配物理页 → 建立页表映射 → 写入成功。

六、嵌入式场景的特别之处

从你之前的对话看,你可能也关注 ESP32 等嵌入式平台。这类平台的内存申请原理与 Linux 有显著不同:

  • 无 MMU 或仅有简单 MPU:通常没有虚拟内存和缺页中断,物理地址与虚拟地址一一对应(或线性偏移)。
  • 静态分区或简单堆:FreeRTOS 的 pvPortMalloc() 通常基于**首次适应(First Fit)**算法,碎片问题更突出。
  • 内存总量极小:没有"延迟分配"的奢侈,malloc 失败是常态,嵌入式开发更推荐静态分配内存池(Memory Pool)

总结

内存申请的核心原理是:虚拟地址空间的记账 + 物理页面的延迟映射 + 用户态分配器的缓存与复用。理解这三层(应用层→C库→内核→硬件MMU),就能解释从 malloc(1) 到申请 1GB 内存时系统发生的全部行为。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐