macos 内存分配学习笔记
字数 2218 2025-08-24 07:48:09
macOS 内存分配学习笔记
基础概念
在 macOS 中利用的是 libcmalloc 内存分配器,堆块分为三种类型:
-
tiny (Q = 16)
- 大小范围:tiny < 1009B
- 最小单位为
quantum,大小为 0x10 字节 - 记录堆块大小的信息为以
quantum为单位的msize
-
small (Q = 512)
- 大小范围:1008B < small < 127KB
block在 region 中的大小为 0x200
-
large
- 大小范围:127KB < large
每个 magazine 有个 cache 区域可以用来快速分配释放堆。
内存结构
整体结构
每个进程包含 3 个区域:
- tiny rack (1MB)
- small rack (8MB)
- large allocations
每个区域里面包含多个 magazine 区域(活动可变),magazine 区域中又包含多个 Region。
Region 结构
Region 包含三部分:
- 以 Q 为单位的内存 block
- 负责将各个 Region 关联起来的 trailer
- 记录 chunk 信息的 metadata
tiny Region 结构
tiny Region {
Q (1 Q = 16) * 64520 个
region_trailer_t trailer
metadata [64520 / sizeof(uint32_t)] {
bitmaps[0]: uint32_t header = 描述哪个 block 是起始 chunk
bitmaps[1]: uint32_t inuse = 描述 chunk 状态 (busy/free)
}
}
small Region 结构
Small Region {
Q (1 Q = 512) * 16320 个
region_trailer_t trailer
metadata [16320] {
bitmaps[0]: uint16_t msize = 最高一位描述 chunk 状态 (busy/free), 其余位描述 chunk 的 Q 值
}
}
tiny 堆块管理
tiny chunk 特性
- 类似于 Linux 下的堆块
- Free 后会被放在对应的 freelist 上
- Free 后的 chunk 会写入数据:
- checksum(prev_pointer)
- checksum(next_pointer)
- msize
指针校验机制
- 使用 checksum 保护指针:
Checksum = SumofEverybytes(ptr ^ cookie) & 0xf- 随机生成一个 cookie
- 将
(checksum << 56) | (pointer >> 4)保存指针
tiny magazine 结构
map_last_free: 保存最后一次 free 的 chunkmap_last_free_msize: 保存最后一次 free 的 chunk 大小map_last_free_rgn: 保存最后一次 free 的 chunk regionfree_list: 以 0x10 为单位的 64 个链表,类似于 Linux 的 smallbins
tiny 分配机制
- 检查 tiny magazine 中的
mag_last_free_msize是否匹配 - 如果不匹配,从 free_list 中查找
- 对找到的 chunk 的 next 指针进行解密
- 如果没有合适大小的 chunk,从能容纳的 list 中取出并分割
- 如果 free_list 中没有可用 chunk,从 tiny region end 中申请
- 更新 metadata 并返回 chunk
tiny 释放机制
- 如果 msize < 0x10,与 cache 中的 freed 堆块交换
- 执行
tiny_free_no_lock进行合并操作:- 利用上一个 freed chunk 末尾的 msize 定位上一个 freed chunk
- 利用当前 chunk 的 msize 定位下一个 freed chunk
- 合并前执行 unlink 操作
small 堆块管理
small chunk 结构
- 与 tiny 不同,pre 和 next 指针使用原始指针
- pre 和 next 指针后有 1 字节存储 checksum 值(填充为 8 字节)
- 存在 oob free chunk 用于页面对齐,没有元数据
small magazine 结构
- 前面的
map_last_xxx作为 cache free_list以 0x200 为单位,保存oob_free_entryoob_free_entry保存在 small Region 末尾
small Region 详细结构
small_region_endsmall_meta_wordssmal_oob_free_entries- block 大小为 0x200
- region 默认大小为 0x800000
- block 个数为 16319
small 分配机制
- 检查 magazine 中的 cache 是否匹配
- 从 free_list 中查找
- 对普通 chunk 执行 unchecksum next 指针
- oob chunk 直接使用原始数据
- 执行 unlink 操作,验证
next->prev == ptr - 修改 small Region 末尾的 metadata
small 释放机制
- 把 chunk 放入 cache
- 根据 prev 指针和 metadata 定位上一个堆块
- 定位下一个堆块
- 合并相邻的 freed chunk
- 执行 unlink 操作前验证
prev->next == next->prev == ptr
安全机制
- 使用 checksum 保护指针,防止堆元数据被溢出破坏
- 随机生成 cookie 用于 checksum 计算
- 合并前严格验证链表完整性
性能优化
- 使用 cache 机制提高分配速度
- 采用类似 slab 的分级管理
- 通过 magazine 实现多线程高效管理