A02 — Internal Structure of the Heap: Arena, Bin, and Chunk

Key Insight: The heap managed by malloc is a precision system of bins and chunks.


Structure of malloc_chunk

Bash
malloc 把内存分成 chunk(块)来管理:

struct malloc_chunk {
    INTERNAL_SIZE_T prev_size;   // 前一个 chunk 的大小(如果前一个空闲)
    INTERNAL_SIZE_T size;       // 本 chunk 大小 + 标志位
    malloc_chunk *fd;           // 同 bin 的下一个 chunk
    malloc_chunk *bk;           // 同 bin 的上一个 chunk
    // ... (用户数据从这里开始)
};

prev_size 的特殊用法:
  - 如果前一个 chunk 是空闲的:prev_size = 前一个 chunk 的大小
  - 如果前一个 chunk 是已分配的:prev_size 可以存储用户数据(复用)

size 的低 3 bits 是标志位:
  - PREV_INUSE (0x1):前一个 chunk 已被分配
  - IS_MMAPPED (0x2):chunk 通过 mmap 分配
  - NON_MAIN_ARENA (0x4):chunk 不属于 main arena

最小 chunk(32-bit):32 bytes
  - prev_size:4 bytes
  - size:4 bytes
  - fd:4 bytes
  - bk:4 bytes
  - 合计 16 bytes... 但需要对齐到 8 字节,所以实际 32 bytes

Structure of Bins

Bash
Bin = 空闲 chunk 的链表

glibc 有多种 bin:

1. Fast bins(fastbin):
   - 大小:16, 24, 32, 40, 48, 56, 64 bytes(共 7 个)
   - 单向链表(fd only,无 bk)
   - LIFO(后进先出)
   - 不会被合并(保持大小)
   - 分配 O(1),释放 O(1)

2. Small bins:
   - 大小:32, 40, 48, ... ~ 1024 bytes(每 8 bytes 一个,共 62 个)
   - 双向循环链表(fd + bk)
   - 按大小排序
   - 合并相邻空闲块

3. Large bins:
   - 大小:>= 1024 bytes
   - 双向循环链表
   - 按大小排序(递减)
   - 每个 bin 管理一个大小范围内的 chunk

4. Unsorted bin:
   - 大小:任意(刚释放的块在这里)
   - 双向循环链表
   - 分配时会先检查 unsorted bin(可能返回小块)

Bash
Bin 的数据结构:

struct malloc_state {
    // fastbins
    mfastbinptr fastbinsY[NFASTBINS];  // 7 个 fastbin 头

    // smallbins + largebins
    malloc_chunk *bins[NBINS * 2 - 2]; // 124 个 bin 头(双向链表)

    // unsorted bin
    malloc_chunk *bins[2];  // unsorted bin 头
};

bins[i] 是链表的头节点:
  - bins[2*(i-NFASTBINS)] = fd(前向指针)
  - bins[2*(i-NFASTBINS)+1] = bk(后向指针)

Allocation and Free Flow

Bash
malloc(size) 分配流程:

1. 计算实际分配大小(size + overhead + 对齐)
2. 进入对应 size 的 fastbin 或 smallbin
3. 如果 bin 不空,取出第一个 chunk(O(1))
4. 如果 bin 空,检查 unsorted bin
5. 如果还空,合并 fastbin 到 smallbin
6. 如果仍然空,向 top chunk 要空间
7. top chunk 不够,调用 brk 扩展堆

示例:malloc(32)

  32 -> 48 bytes(对齐到 8)
  → fastbin[5](32-byte chunk 的 fastbin)
  → 如果 fastbin[5] 非空,分配

---

free(ptr) 释放流程:

1. 检查 ptr 是否在 fastbin 范围(<= 64 bytes)
2. 如果是,插入 fastbin(O(1))
3. 如果不是,合并相邻的空闲 chunk
4. 放入 unsorted bin 或对应 size 的 bin

合并(consolidate):
  - 检查下一个 chunk 是否空闲 → 合并
  - 检查上一个 chunk 是否空闲(如果 PREV_INUSE=0)→ 合并
  - 合并后放入 unsorted bin

注意:top chunk 不会和相邻 chunk 合并
  - top chunk 是堆顶
  - 只有 top chunk 可以 shrink(通过 brk)

Top Chunk and Remainder

Bash
Top chunk 的特殊性:

top chunk = 堆顶的剩余空间
  - 不属于任何 bin
  - 是最后的后备空间

分配流程:
  1. 所有 bin 都空
  2. 向 top chunk 要空间
  3. 如果 top chunk >= 需要的大小:
     - 分配出去
     - 剩余部分成为新的 top chunk
  4. 如果 top chunk 不够:
     - 调用 brk() 扩展堆
     - 新的 top chunk = old top + new space

remainder(剩余):

假设:
  top chunk = 1024 bytes
  malloc(100) = 需要 128 bytes(含 overhead)

  分配后:
    分配出去:128 bytes
    top chunk 剩余:1024 - 128 = 896 bytes
    → 896 bytes 成为新的 top chunk

如果 top chunk 太小(< 128 bytes):
  - 调用 brk 扩展
  - 不产生 remainder

/proc/self/maps: Viewing Heap Structure

Bash
$ cat /proc/self/maps | grep heap

00...-00... rw-p [heap]

$ cat /proc/self/1/smaps | grep -A 20 "[heap]"

00...-00...
  Rss:            132 kB
  Pss:            132 kB
  Shared_Clean:    0 kB
  Shared_Dirty:    0 kB
  Private_Clean:  12 kB
  Private_Dirty: 120 kB
  Ref 1

heap 区域是 brk 管理的区域。

$ cat /proc/self/smaps | grep -E "Anon|Heap"

00007f... rw-p [heap]
  AnonHugePages:     0 kB
  Anonymous:       8192 kB   ← 堆使用的匿名页

对比 mmap 区域:
00007f... rw-p [anon]
  Anonymous:      16384 kB   ← mmap 匿名映射使用的页

Summary

  • malloc_chunk: prev_size + size (lower 3 bits are flags) + fd + bk + user data
  • Bin Types: fastbin (7, singly-linked LIFO) / smallbin (62, doubly-linked) / largebin (63) / unsorted (merge staging)
  • Allocation Flow: fastbin → smallbin → unsorted → top chunk → brk extension
  • Free Flow: free → check coalescing → place in fastbin or unsorted
  • Top chunk: Last resort; splitting produces a remainder; never merges with adjacent blocks

Next (A03): Why does frequent malloc/free slow down in multi-threaded programs? Arena contention and per-thread arena design.


Last modified: 2024年8月10日

Author

Comments

Write a Reply or Comment

Your email address will not be published.