块分配器、堆栈分配器和临时分配器之间有什么区别?

dol*_*hin 7 c c++ memory-management game-engine allocator

迈克·阿克顿 (Mike Acton)在他的演讲“为引擎开发人员解决正确的问题”中说

绝大多数时候,您所需要的只是这三种类型的分配器:块分配器、堆栈分配器和临时分配器

但是,他没有详细说明这些类型的分配器之间的差异。

我认为“堆栈分配器”只是一个基于堆栈的分配器,但我听说过的所有其他类型(包括“arena”)听起来像是做同样事情的奇特方法,即“分配一个大块”并以一种有效的方式将其分成块,然后在完成后释放它”

那么,这些分配器之间有什么区别,各自的优点是什么,为什么我“绝大多数时候”只需要这三个?

Com*_*sMS 14

正如评论中指出的那样,演讲中使用的术语在行业中尚未得到很好的确立,因此对于这里所指的具体分配策略存在一些疑问。考虑到游戏编程文献中经常提到的内容,这是我有根据的猜测这三个分配器背后的含义:

\n

块分配器

\n

也称为池分配器。这是一个分配器,仅分配固定大小的内存块,无论用户实际请求多少内存。

\n

假设您有一个块大小为 100 字节的块分配器。您想为单个 64 位整数分配内存吗?它给你一个 100 字节的块。您想为 20 个单精度浮点数的数组分配内存吗?它给你一个 100 字节的块。您想为 101 个字符的 ASCII 字符串分配内存吗?它会给你一个错误,因为它无法将你的字符串放入 100 个字节。

\n

块分配器有几个优点。它们相对容易实现,并且不会受到外部内存碎片的影响。它们通常还表现出非常可预测的运行时行为,这对于视频游戏来说通常至关重要。它们非常适合大多数分配大小大致相同的问题,而在情况并非如此时,它们显然不太适合。

\n

除了这里描述的最简单的版本(其中每个分配器仅支持单个块大小)之外,还存在更灵活的扩展,支持多个块大小,而不会过多地损害上述优点。

\n

堆栈分配器

\n

堆栈分配器的工作方式与堆栈类似:只能以与分配相反的顺序取消分配。如果您随后分配对象,A则无法在不放弃的情况B下回收内存。AB

\n

堆栈分配器非常容易实现,因为您只需要跟踪一个标记已使用和未使用的内存区域之间的分隔的指针。分配将该指针移动到一个方向,而释放则将其向相反的方向移动。

\n

堆栈分配器可以最高效地利用内存,并具有完全可预测的运行时行为。显然,它们仅适用于很容易实现所需的解除分配顺序的问题。静态地强制执行正确的释放顺序通常并不简单,因此如果不小心使用它们,调试它们可能会很痛苦。

\n

临时分配器

\n

也称为单调分配器。临时分配器的工作方式与堆栈分配器类似。分配的工作原理完全相同。释放是一个空操作。也就是说,内存一旦分配就无法回收。

\n

如果你想取回内存,你必须销毁整个临时分配器,从而立即释放其所有内存。

\n

暂存分配器的优点与堆栈分配器相同。它们非常适合解决您可以自然地识别不再需要所有已分配对象的点的问题。与堆栈分配器类似,如果不小心使用,如果在仍有活动对象存在时分配器被销毁,它们可能会导致严重的运行时错误。

\n

为什么我只需要这三个?

\n

经验表明,在许多领域中,不需要完全动态的内存管理。分配生命周期可以按公共大小(块分配器)或公共生命周期(临时和堆栈分配器)进行分组。如果在这样的领域工作的工程师愿意经历对每个分配进行相应分类的麻烦,他们可能只需使用这三种分配策略就可以满足大部分动态内存需求,而无需引入不合理的额外开发工作。作为对他们努力的奖励,他们将受益于这些算法良好的运行时特性,特别是非常快且可预测的执行时间以及可预测的内存消耗。

\n

如果您所在的领域很难按照这些术语对分配进行分类;或者如果您不能或不愿意花费额外的工程工作;或者,如果您正在处理一个不能很好地映射到这三个分配器的特殊用例 - 您可能仍然想使用通用分配器,即良好的旧 malloc。

\n

谈话中提出的要点更多的是如果你这样需要担心自定义内存分配 - 特别是在具有特定要求和权衡的视频游戏领域 - 这三种类型的分配器是解决以下问题的非常好的答案当 na\xc3\xafvely 单独依赖通用分配器时,您可能会遇到特定问题。

\n

对 C++ 中的分配器进行了长篇演讲不久前,

\n