前置
进程堆区位于 BSS 段之后,自低地址向高地址增长。 内核通过 mm_struct 中的 program break 标记堆的上边界。 用户态动态内存扩容,内核只提供两种底层方式:brk 主堆扩容、mmap 匿名映射扩容。
1. brk /sbrk(小块内存)
函数原型与返回值
// brk 系统调用
int brk(void *addr);
参数
addr:想要设置的 program break 目标虚拟地址。返回值:成功返回 0;失败返回 -1。
作用:直接把堆边界设置到指定绝对地址。
// sbrk库函数,封装brk
void *sbrk(intptr_t increment);
参数
increment:地址增量。正数扩大堆;负数缩小堆;传 0 获取当前堆边界。返回值:成功返回扩容之前旧的 program break 地址;失败返回
(void*)-1。
题外话:函数VS系统调用
机制
扩容:抬高 program break,仅拓宽虚拟地址空间,无物理内存分配;首次访问缺页、分配物理页。
释放:free 只标记内存空闲,默认不回缩堆、不归还内核,仅堆顶超大空闲时才回缩边界。
特点
虚拟内存连续一整块
容易产生内存碎片
系统调用少、速度快
适用:小于 128KB 小块内存
2. mmap 匿名映射(大块内存)
函数原型与返回值
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数
addr:期望映射起始地址,传 NULL 由内核自动分配length:映射字节长度prot:内存权限(可读、可写等)flags:映射类型,匿名映射使用MAP_ANONYMOUS | MAP_PRIVATEfd:匿名映射填 - 1offset:匿名映射填 0
返回值:成功返回映射区域起始虚拟地址;失败返回
MAP_FAILED(宏,本质是(void*)-1)
配套释放接口
int munmap(void *addr, size_t length);
参数:映射起始地址、长度
返回值:成功返回 0,失败返回 - 1
机制
分配:新建独立虚拟内存,访问缺页才分配物理页
释放:
munmap直接解除映射、立刻归还内核
特点
每块内存离散、独立
无内存碎片
每次分配释放都触发系统调用,开销更高
适用:大于等于 128KB 大块内存
3. 核心对比
4. 终极总结
brk 接收绝对地址设置堆边界;sbrk 传入增量移动边界,返回旧断点地址。mmap 创建独立匿名虚拟区域,munmap 释放。
brk 靠移动堆边界扩容,连续高效但不主动释放,有碎片。
mmap 新建独立虚拟内存,释放彻底无碎片,但开销更大。