Block、Stack与Scratch分配器有何差异?各自优势及适用场景是什么?
三种核心内存分配器:差异、优势与适用场景
Mike Acton提到的这三种分配器,是引擎开发中经过验证的高效内存管理工具,它们的核心差异、优势和适用场景如下:
Stack Allocator(栈分配器)
- 核心逻辑:模拟系统栈的「后进先出(LIFO)」行为,分配时仅需移动栈顶指针,释放时必须从最后分配的对象开始倒序释放,或者一次性清空整个分配器的内存块。可基于自定义内存池实现,不一定依赖线程的系统栈。
- 优势:分配/释放操作几乎零开销;天然保证内存对齐;完全不会产生内存碎片。
- 适用场景:生命周期严格嵌套的对象,比如函数内的临时变量、渲染管线中嵌套阶段的临时缓存、某一帧内创建且帧末销毁的资源。
Block Allocator(块分配器)
- 核心逻辑:预先将大块内存划分为固定大小的内存块,按块的大小维护不同的空闲链表。分配时直接从对应大小的空闲链表中取出一块;释放时将块放回对应链表即可。和通用arena分配器的核心区别是:arena支持任意大小的拆分,而block分配器只处理固定尺寸的块。
- 优势:分配/释放速度极快(仅链表操作);内存碎片可控(固定大小块不会产生细碎的无法利用的碎片);适合批量管理同类型对象。
- 适用场景:大量同类型对象的频繁创建销毁,比如游戏中的实体组件、粒子系统的粒子实例、网络数据包对象。通常会维护多个不同尺寸的块池(如8B、16B、32B、64B),覆盖引擎中常用的对象大小。
Scratch Allocator(临时分配器)
- 核心逻辑:属于特殊的栈式分配器,专门用于单次生命周期的临时内存。分配时同样移动指针,释放时不需要逐个倒序释放,直接将指针重置到初始位置即可一次性清空所有分配的内存。
- 优势:比普通栈分配器更灵活,无需严格遵循LIFO释放顺序;分配速度同样极致;内存使用完即可一次性回收,无需跟踪单个对象。
- 适用场景:一帧内的临时计算数据(如AI路径规划的临时节点数组)、渲染中的临时顶点/索引缓存、序列化/反序列化的临时缓冲区。
为什么这三种分配器就能覆盖绝大多数引擎开发场景?
引擎开发的内存需求有极强的规律性,这三种分配器刚好命中了核心场景:
- 临时内存需求占比极高:引擎中大量内存是单次帧内或单次调用的临时数据,Stack和Scratch分配器可以用极致的性能处理这类需求,完全替代低效的系统malloc。
- 同类型对象频繁操作:游戏实体、组件、粒子等都是高频创建销毁的同类型对象,Block分配器完美解决这类场景的碎片问题和性能开销。
- 简化内存管理复杂度:过多的分配器类型会增加代码维护成本,这三种分配器的组合已经能覆盖90%以上的性能敏感场景,剩下的极少数随机分配场景直接用系统默认分配器即可,不需要额外造轮子。
内容的提问来源于stack exchange,提问作者doliphin
相关产品推荐
相关产品推荐

