跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 内存管理:malloc 底层原理与实现

C++ 内存管理中 malloc 函数通过 brk/sbrk 和 mmap 系统调用实现。小内存使用 brk/sbrk 扩展堆,大内存使用 mmap。ptmalloc 作为内存池管理机制,将内存划分为 chunk 进行管理。chunk 包含大小、指针等字段,空闲时维护双向链表。分配时优先查找空闲链表,不足时扩展堆或 mmap。释放时标记空闲并合并相邻块,必要时归还操作系统。

CoderByte发布于 2026/3/20更新于 2026/5/2428 浏览
C++ 内存管理:malloc 底层原理与实现

一、内存布局

图 1:内存布局示意图(栈向下扩展,堆向上扩展)

由上图可知,栈至顶向下扩展,堆至底向上扩展。

二、brk(sbrk)和 mmap 函数

1. brk(sbrk)

在 Linux 系统中,malloc 底层主要通过 brk、sbrk 和 mmap 这几个系统调用来实现内存的分配和管理。

#include <unistd.h>

void *brk(const void *addr);
void *sbrk(intptr_t incr);

两者的作用是扩展 heap 的上界 brk。

  • brk():参数设置为新的 brk 上界地址,成功返回 0,失败返回 -1;如果 addr 大于当前的程序中断点,就会扩大数据段,分配新的内存;如果 addr 小于当前的程序中断点,就会缩小数据段,释放内存。
  • sbrk():参数为申请内存的大小,返回 heap 新的上界 brk 的地址;当 increment 为正数时,堆顶指针会向高地址移动,分配内存;当 increment 为负数时,堆顶指针会向低地址移动,释放内存。

2. mmap

#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

mmap() 进行内存分配(malloc)时一般使用后者,前者主要是进行文件映射。 mmap 分配内存比较直接,相对的开销也较大,释放也比较简单,通过 munmap 函数可以立即将内存归还给操作系统。

一般来说,当 malloc 申请的内存较小时,会使用 brk 或 sbrk 来扩展堆内存;而当申请的内存较大时(通常阈值为 128KB),会直接使用 mmap 在内存映射区申请内存。

但是,如果每次申请内存都调用这些接口的话,势必会影响系统的性能,并且也极容易产生内存碎片。所以 malloc 采用 ptmalloc(内存池管理机制)对内存的分配与回收进行管理。

3. ptmalloc

ptmalloc 会预先向操作系统申请一块内存供用户使用,当我们申请和释放内存的时候,ptmalloc 会将这些内存管理起来,并通过一些策略来判断是否将其回收给操作系统。

3.1 chunk(内存块基本结构)

在 ptmalloc 中,内存是由一个个 chunk 组成,每个 chunk 由结构体 malloc_chunk 进行描述:

struct malloc_chunk {
    INTERNAL_SIZE_T prev_size; // 前一个 chunk 的大小(如果前一个 chunk 是空闲的)
    INTERNAL_SIZE_T size;      // 当前 chunk 的大小,包括头部开销
    struct malloc_chunk *fd;   // 双向链表指针,指向下一个空闲 chunk(仅当 chunk 空闲时使用)
    struct malloc_chunk *bk;   // 双向链表指针,指向上一个空闲 chunk(仅当 chunk 空闲时使用)
    /* 当前的 chunk 存在于 large bins 中时使用 */
    struct malloc_chunk *fd_nextsize;
    /* 仅当 chunk 空闲时使用 */
    struct malloc_chunk *bk_nextsize;
};
  • prev_size:表示前一个空闲的 chunk 大小,如果前一个 chunk 不空闲,该字段无意义,prev_size 主要用于相邻空闲 chunk 的合并。
  • size:当前 chunk 的大小和一些其他信息,其中低三位(A,M,P)中记录包括前一个 chunk 是否在使用中,当前 chunk 是否是通过 mmap 获得的内存,当前 chunk 是否属于非主分配区。
  • fd 和 bk:这两兄弟只有当该 chunk 块空闲时才存在,其作用是用于将对应的空闲 chunk 块加入到空闲 chunk 块链表中进行管理。例如,当一个 chunk 被释放时,它会根据 size 字段中的信息找到前一个和后一个 chunk,判断它们是否空闲,如果空闲则进行合并,然后将合并后的 chunk 通过这两个指针插入到相应的空闲链表中。如果该 chunk 块被分配给了应用程序使用,那么这两个指针被当作应用程序的使用空间,不会浪费。
  • fd_nextsize 和 bk_nextsize:和 fd、bk 类似。

注意:当 chunk 为空时才有 fd、bk、fd_nextsize、bk_nextsize 四个指针,当 chunk 不为空,这四个指针的空间是直接交给用户使用的。

3.2 malloc 分配大体流程
  1. 搜索空闲链表:首先,ptmalloc 会在快速链表(fast bins)(小于 4 字节)、小链表(small bins)和大链表(large bins)中查找是否有合适大小的空闲 chunk。快速链表用于管理小且常用大小的 chunk,这些 chunk 在释放时不会与相邻的 chunk 合并,分配速度很快;小链表中的 chunk 大小固定且不超过 512 字节,按大小顺序排列;大链表中的 chunk 大小大于 512 字节。如果在这些链表中找到了合适的 chunk,就直接返回该 chunk 给用户。
  2. 扩展堆或使用 mmap:如果在空闲链表中没有找到合适的 chunk,ptmalloc 会尝试从堆顶的 top chunk 中分配内存。(top chunk 相当于分配区的顶部空闲内存)如果 top chunk 的大小足够,就从 top chunk 中分割出一块满足需求的内存返回给用户,剩余部分成为新的 top chunk;如果 top chunk 的大小不足,且申请的内存小于一定阈值(如 128KB),ptmalloc 会调用 sbrk 扩展堆内存,然后从新扩展的内存中分配;如果申请的内存大于阈值,ptmalloc 会使用 mmap 将内存放到 mmaped chunk 上,当释放 mmaped chunk 上的内存的时候会直接交还给操作系统。
  3. 大块拆分:当从大链表中找到一个大于请求大小的 chunk 时,ptmalloc 会将该 chunk 拆分成两部分,一部分满足请求大小返回给用户,另一部分作为剩余块(remainder chunk),根据其大小插入到合适的空闲链表中。
3.3 释放流程

当调用 free 释放内存时,ptmalloc 会执行以下操作:

  1. 标记为空闲:首先将释放的 chunk 标记为空闲状态,并根据 chunk 的大小和标志位判断是否需要与相邻的空闲 chunk 进行合并。
  2. 合并相邻空闲块:如果当前 chunk 的前一个和后一个 chunk 都是空闲的,ptmalloc 会将它们合并成一个大的 chunk,减少内存碎片。合并后的 chunk 会被插入到合适的空闲链表中。
  3. 缩小堆:如果释放的 chunk 与 top chunk 相邻,且合并后的 top chunk 足够大(超过一定阈值),ptmalloc 会调用 sbrk 缩小堆内存,将多余的内存归还给操作系统。

目录

  1. 一、内存布局
  2. 二、brk(sbrk)和 mmap 函数
  3. 1. brk(sbrk)
  4. 2. mmap
  5. 3. ptmalloc
  6. 3.1 chunk(内存块基本结构)
  7. 3.2 malloc 分配大体流程
  8. 3.3 释放流程
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于 Jetson 与 OpenClaw 构建飞书 AI Agent 远程交互系统
  • Java 电子招投标采购系统核心功能与业务流程设计
  • 26 年网络建设与运维样题一网络建设与调试模块完整配置方案
  • Java 大数据在智能家居能源消耗趋势预测与节能策略优化中的应用
  • Mac Big Sur 使用 Docker 运行 OpenCode 实现 AI 自动化开发
  • Spring AI 实战:基于 Ollama 构建离线私有化 AI 服务
  • GitHub Copilot 提示词工程实战指南:从基础到精通
  • 昇腾平台下 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践
  • ComfyUI 源码部署:基于 Python Embed 版本构建独立整合包
  • 昇腾平台 DeepSeek-R1 与 Qwen2.5 强化学习训练优化实践
  • Copilot Pro 使用指南:模型选择与配额管理策略
  • OpenClaw 智能体生态布局与核心能力解析
  • Android 离线应用核心组件解析:dev-summit-architecture-demo 架构
  • OpenClaw 多端交互实战指南:Web/TUI/钉钉集成详解
  • 2025 年技术博客创作总结与 2026 年规划
  • Web 项目自动化测试实战:从零搭建博客系统 UI 框架
  • Java 项目中的 .idea 与 target 文件夹
  • OpenClaw 实战:统一接入飞书/钉钉/WhatsApp,打造 AI 指挥中心
  • Stable Diffusion 视觉提示词注入攻击原理与实现
  • Kimi K2.5 开源部署与 API 接入实战教程

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online