heap concept
2026/9/8 15:49:17 网站建设 项目流程

堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。一般称管理堆的那部分程序为堆管理器,堆管理器处于用户程序与内核中间。

在内存分配与使用过程中,Linux基本内存管理思想,只有当真正访问一个地址的时候,系统才会建立虚拟页面与物理页面的映射关系。虽然操作系统已经给程序分配了很大的一块内存,但这块内存其实只是虚拟内存。只有当用户使用到相应的内存时,系统才会通过缺页真正分配物理页面给用户使用。

Ptmalloc2 主要是通过malloc/free函数来分配和释放内存块

malloc 返回对应大小字节的内存块的指针,也对一些异常情况进行了处理:
malloc(n)

  • 当 n = 0 时,返回当前系统允许的堆的最小内存块。
  • 当 n 为负数时,size_t是无符号数,所以程序会申请很大的内存空间,但通常都会失败,因为系统没有那么多的内存可以分配。

free 函数会释放由 p 所指向的内存块。这个内存块有可能是malloc函数得到的,也有可能是通过相关的函数realloc得到的。同样也对异常情况进行了处理:

free(p)

  • 当 p 为空指针时,函数不执行任何操作。
  • 当 p 已经被释放之后,再次释放会出现乱七八糟的效果,其实就是double free。
  • 除了被禁用(mallopt)的情况下,当释放很大的内存空间时,程序会将这些内存空间还给系统,以便于减小程序所使用的内存空间。

分配背后的系统调用


申请和释放内存时并不真正与系统交互,背后的系统调用主要是(s)brk以及mmap、munmap函数。

(s)brk


初始时,堆的起始地址start_brk以及堆的当前末尾brk指向同一地址。是否开启ASLR,两者的具体位置会有所不同:

  • 不开,start_brk以及brk会指向data/bss段的结尾。
  • 开启,start_brk以及brk也会指向同一位置,只是这个位置是在data/bss段结尾后的随机偏移处。

在不同的上下文里,“堆”的含义差别很大,分两层来理解。


第一层:操作系统层面的“堆” (Heap Segment)

这是从进程的虚拟内存布局角度来看的。

  • 是什么:在 Linux 进程中,内存被划分为不同的段(Segment),其中有一个区域叫“堆段(Heap Segment)”。它位于数据段(BSS)之后,向高地址方向生长。

  • 管理者:这个区域由操作系统内核管理,通过brksbrk系统调用,可以调整堆段的“顶部边界”(即 program break)。

  • 特点

    • 这是进程最早获得的动态内存区域。

    • 它是一段连续的虚拟地址空间

    • 进程启动时,堆段只有很小的初始大小,随着程序不断申请内存,堆段会逐渐扩大(brk向上推)。

一句话总结:操作系统层面的“堆”,是进程虚拟地址空间中,一块可以被程序动态扩充和收缩的连续内存区域,通过brk/sbrk来管理。


第二层:内存分配器层面的“堆” (Heap)

这是从ptmalloc2内存分配器的角度来看的。在 Glibc 中,malloc管理的内存并不局限于操作系统的那个“堆段”。

  • 广义的堆:在ptmalloc2的语境下,“堆”是一个更广义的概念,它指的是所有由内存分配器管理并提供给malloc使用的动态内存池。它包含两种来源:

    1. 主分配区(main_arena)的堆:这直接对应操作系统层面的那个堆段,通过brk/sbrk扩展。这就是我们传统意义上说的“堆”。

    2. 非主分配区(thread arena)的堆:这些是通过mmap系统调用从操作系统匿名映射来的内存区域。它们在虚拟地址空间中是独立的、不连续的,并不在传统的“堆段”内。

  • 管理单位:在ptmalloc2中,这些不同来源的内存块,被抽象成统一的heap_info结构来管理(非主分配区的每个堆段都有一个heap_info头部)。对于主分配区,因为它只有一个堆段,所以不需要heap_info结构。

一句话总结ptmalloc2中的“堆”,是内存分配器管理的“内存池”的泛称,它可能来自brk堆段,也可能来自mmap映射区域。


第三层:数据结构层面的“堆” (Chunk & Top Chunk)

更具体地说,在ptmalloc2的源码和运行机制中,“堆”还指代两个具体的数据结构实体:

  1. Top Chunk

    • 每个 Arena(分配区)都有一个Top Chunk。它位于该 Arena 当前活跃堆段的最顶部(最高地址处)。

    • 它是该 Arena 的“最后储备金”。当所有空闲链表(Bin)都无法满足分配请求时,malloc会从 Top Chunk 中直接切割内存返回。

    • 当 Top Chunk 空间不足时,主分配区会通过brk扩展堆段,非主分配区会通过mmap创建新的堆段,从而获得新的 Top Chunk。

  2. Heap Segment(堆段)

    • 对于非主分配区,每个通过mmap映射来的连续内存区域,称为一个“堆段”

    • 堆段的头部有一个heap_info结构,记录了它的大小、属于哪个 Arena,以及指向前一个堆段的指针。

    • 一个非主分配区可以有多个堆段,它们通过heap_info中的prev指针链接成一条单向链表。


三者关系图

+---------------------------------------------------------+ | 进程虚拟地址空间 | +---------------------------------------------------------+ | 代码段 | 数据段 | 堆段 (brk) | 内存映射段 (mmap) | 栈段 | +--------------------+----------+-------------------+-----+ | | | ▼ ▼ ▼ +-----------+ +-----------+ +--------------+ | main_arena| | thread_arena| | thread_arena| +-----------+ +-----------+ +--------------+ | | | ▼ ▼ ▼ +-----------+ +-----------+ +-----------+ | Top Chunk | | heap_info | | heap_info | | (brk堆段) | | (mmap堆段1)| | (mmap堆段2)| +-----------+ +-----------+ +-----------+ ▲ ▲ ▲ | | | └──────┬───────┴──────────────────┘ │ ▼ 所有堆段共同构成了 ptmalloc2 管理的“堆”

终极一句总结

“堆”在 Glibc 的语境下,是一个层叠的概念:

  1. 从内核看,它是通过brk管理的一段连续虚拟内存区域(堆段)。

  2. 从分配器看,它是main_arenabrk堆段和所有thread_arenammap堆段的总集合

  3. 从数据结构看,它是每个 Arena 顶部的Top Chunk,以及构成这些 Top Chunk 的多个heap_info堆段

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询