4.0 KiB
4.0 KiB
Allocator Selection And Current Limits
为什么引擎要有多种分配器
商业级引擎不会把所有内存都交给一套通用堆分配器。原因很现实:
- 帧级临时数据的生命周期极短,逐块释放反而是额外成本。
- 大量固定尺寸对象如果走通用堆,碎片和元数据开销通常不划算。
- 调试阶段又往往需要单独统计某个系统到底分了多少内存。
这就是 XCEngine::Memory 当前这组 API 的设计出发点。
现在这几个分配器分别适合什么
LinearAllocator
适合:
- 一整批数据统一释放
- 帧级或阶段级临时内存
- 极低开销的顺序分配
不适合:
- 需要单个对象独立释放
- 需要通用
realloc
如果你熟悉 Unity 或其它引擎,可以把它理解为更接近 arena / frame allocator 这一类思路,而不是常驻对象堆。
PoolAllocator
适合:
- 固定尺寸或尺寸上界稳定的对象
- 高频分配 / 释放
- 想避免通用堆碎片
不适合:
- 大小变化大的对象
- 需要可变尺寸重分配
ProxyAllocator
适合:
- 给现有分配器加统计视图
- 调试某个系统的内存用量
- 不想修改底层分配器实现,但想额外记录指标
不适合:
- 被当作完整可信的内存分析器
当前版本最需要诚实面对的事
这套模块已经有了不错的 API 形状,但实现还没有完全跟上。
最重要的限制有这些:
LinearAllocator::Free和Reallocate还没有完成。PoolAllocator::Reallocate还没有完成。ProxyAllocator的释放统计当前不准确。MemoryManager的泄漏和报表接口现在只是占位输出。- 部分统计方法名字很强,但当前返回值更接近“固定上界”而不是真实历史峰值。
- 通过
MemoryManager创建且依赖系统分配器的对象,目前仍要求调用方在Shutdown()之前主动销毁。
这意味着当前更合理的使用方式是:
- 把这些分配器当作明确生命周期的工具型组件;
- 不要把它们当成已经完备的内存诊断平台。
如何选择
如果你只是要一块批量临时内存:
auto allocator = XCEngine::Memory::MemoryManager::Get().CreateLinearAllocator(1024 * 1024);
如果你要大量固定尺寸对象:
auto allocator = XCEngine::Memory::MemoryManager::Get().CreatePoolAllocator(sizeof(MyNode), 1024);
如果你要统计某个系统的分配情况:
auto allocator = XCEngine::Memory::MemoryManager::Get().CreateProxyAllocator("RenderGraph");
这里还要额外记住一点:
CreateProxyAllocator()应在Initialize()之后调用。CreateLinearAllocator()如果绑定了系统分配器作为父分配器,也应在Shutdown()前完成销毁。
为什么要保留 IAllocator
IAllocator 的价值不在“虚函数本身”,而在于它允许上层容器和子系统表达“我需要一个分配策略”,而不是“我必须依赖某个具体实现”。
这和很多商业引擎里的思路一致:
- 容器依赖 allocator 接口
- 上层根据数据生命周期决定用哪种分配器
- 调试阶段可以额外套 tracking / proxy 层
当前阶段最值得补的方向
如果继续把这个模块做实,最值得优先补的通常是:
- 给
LinearAllocator和PoolAllocator明确补完Reallocate/ 回收语义,或者在 API 层彻底禁掉不支持的操作。 - 修正
ProxyAllocator的统计口径。 - 让
MemoryManager的泄漏与报表接口真正产出结构化结果。 - 为不同分配器补充更严格的越界、重复释放和对齐校验。