allocator、自定义内存分配与 pmr 入门
allocator、自定义内存分配与 pmr 入门
时间:2026/04/09
关键词:
std::allocator、allocator_traits、分配与构造分离、状态型分配器、std::pmr
核心目标:理解 STL 容器如何管理内存,以及什么时候值得定制分配器。
1. 为什么 allocator 存在
容器不仅要“放元素”,还要处理:
- 申请原始内存
- 在内存上构造对象
- 销毁对象
- 释放内存
标准库把这层职责抽象成 allocator。
2. std::allocator 的核心职责
可以粗略理解成四步:
allocate(n):分配能容纳n个对象的原始内存construct(...):在指定位置构造对象destroy(...):调用析构deallocate(...):释放内存
现代实现中更常通过:
std::allocator_traits
来统一调度这些接口。
3. 为什么“分配”和“构造”是两件事
因为拿到一块内存,不等于对象已经存在。
1 | T* p = alloc.allocate(4); // 只有内存 |
这也是容器能高效管理未初始化存储区的基础。
4. 一个最小自定义分配器骨架
1 |
|
配合容器使用:
1 | std::vector<int, MyAllocator<int>> v; |
5. 什么时候值得自定义 allocator
不是所有项目都需要。
更常见的适用场景:
- 频繁小对象分配
- 想用内存池减少碎片
- 想把对象放到特定区域
- 需要统计分配行为
- 游戏/服务端有帧级或 arena 分配需求
如果只是普通业务代码,标准分配器通常已经够用。
6. 状态型分配器
有些分配器不仅有类型,还有内部状态,例如:
- 指向某个内存池
- 指向某个 arena
这类分配器要特别注意:
- 拷贝行为
- 容器复制/移动时状态如何传播
这也是 allocator_traits 很重要的原因之一。
7. std::pmr:现代 C++ 更实用的内存资源抽象
C++17 提供了 std::pmr:
- polymorphic memory resource
它把“分配策略”从模板参数层搬到了运行时资源对象层。
最常见组件:
std::pmr::memory_resourcestd::pmr::polymorphic_allocatorstd::pmr::vectorstd::pmr::monotonic_buffer_resource
这通常比手写 allocator 模板更实用。
8. monotonic_buffer_resource 的直觉
这种资源很适合:
- 批量分配
- 很少单独释放
- 整体回收
例如一次请求、一次帧更新、一次解析过程。
好处:
- 分配快
- 局部性好
代价:
- 单个对象通常不能灵活归还给池
9. 和容器性能的关系
allocator 影响的通常不是接口语义,而是:
- 分配次数
- 分配成本
- 碎片
- 局部性
但要注意:
- allocator 优化通常排在算法和数据布局之后
- 不要在没有证据前,把 allocator 当成首要瓶颈
10. 常见误区
10.1 把 allocator 当成“所有性能问题的解药”
很多性能问题其实更可能出在:
- 数据布局
- 扩容策略
- 锁争用
- 随机访问
10.2 只会写 allocate/deallocate,却不理解对象构造时机
allocator 真正重要的是:
- 内存和对象是分开的
10.3 在没有统一资源模型时滥用自定义 allocator
结果容易让代码复杂度大幅上升。
11. 一页总结
allocator 这篇最值得记住的是:
- 容器管理的是“原始内存 + 对象构造/销毁”
- allocator 负责内存来源
allocator_traits是现代实现的核心适配层- 真正工程里,
std::pmr往往比手写 allocator 更实用
如果只记一句:
allocator 优化通常是进阶优化,前提是你已经把算法、数据布局和容器选择做对了。