allocator 与 PMR:把分配策略抽出来 / Allocators and PMR
这一篇偏高级。
如果你刚学完堆、RAII、智能指针,可以先知道 allocator 解决什么问题,不必立刻在业务代码里使用 PMR。
1. allocator 解决的不是“怎么 new 一个对象”
普通代码里,你写:
std::vector<int> values;
values.push_back(1);vector 需要动态存储元素。
默认情况下,它会使用标准分配器。
allocator 的大方向是:
容器负责管理元素生命周期和结构。
allocator 负责提供和回收原始存储。拆开看:
vector wants more capacity
|
v
allocator provides raw storage
|
v
vector constructs elements there
|
v
vector destroys elements when needed
|
v
allocator releases raw storage所以 allocator 和对象生命周期相关,但它不等于对象本身。
2. 为什么需要自定义分配策略
默认分配器已经够普通程序使用。
自定义分配策略通常出现在:
大量小对象频繁分配释放
希望把一批临时对象放在同一块区域
降低内存碎片
统计分配次数和大小
限制模块只能从某个内存池分配
游戏、交易、嵌入式等对延迟敏感场景例如解析一个请求时,会产生很多临时字符串和节点。
请求处理完以后,这些临时对象可以一起释放。
这时逐个 new/delete 成本和碎片可能不理想。
3. PMR 是什么
PMR 是 Polymorphic Memory Resource。
可以先理解成:
把“从哪里分配内存”变成运行时可替换的资源对象。头文件:
#include <memory_resource>PMR 容器例子:
#include <memory_resource>
#include <vector>
int main() {
std::pmr::vector<int> values;
values.push_back(1);
}std::pmr::vector<int> 和普通 std::vector<int> 的接口很像。
差别在于它使用 std::pmr::memory_resource 来分配内存。
4. monotonic_buffer_resource
一个常见资源是:
#include <array>
#include <memory_resource>
#include <vector>
int main() {
std::array<std::byte, 1024> buffer{};
std::pmr::monotonic_buffer_resource resource(
buffer.data(),
buffer.size()
);
std::pmr::vector<int> values{&resource};
values.push_back(1);
values.push_back(2);
}这里大致是:
stack buffer
+--------------------------------+
| 1024 bytes |
+--------------------------------+
^
|
monotonic_buffer_resource
^
|
pmr::vector uses this resourcemonotonic 的意思是分配指针大体向前走。
它通常不逐个回收小块内存。
等资源对象销毁时,再把整体资源释放或结束使用。
这适合“一批临时分配一起结束”的场景。
5. 生命周期陷阱
PMR 最容易出错的不是语法。
而是资源对象的生命周期。
看危险代码:
#include <array>
#include <memory_resource>
#include <vector>
std::pmr::vector<int> make_values() {
std::array<std::byte, 1024> buffer{};
std::pmr::monotonic_buffer_resource resource(
buffer.data(),
buffer.size()
);
std::pmr::vector<int> values{&resource};
values.push_back(1);
values.push_back(2);
return values;
}问题是:
buffer 是局部对象
resource 是局部对象
values 使用 resource 分配元素存储
函数返回后 buffer 和 resource 都没了
返回出去的 values 仍然以为自己有可用资源这就是典型生命周期错误。
PMR 不会自动让资源比容器活得更久。
你必须保证:
memory_resource 的生命周期
至少覆盖所有使用它的容器6. allocator 和 RAII 的关系
allocator 管的是原始存储策略。
容器仍然负责 RAII:
vector 析构
|
v
析构元素
|
v
通过 allocator/resource 归还存储所以 allocator 不是绕开 RAII。
它是让 RAII 容器使用不同的底层存储来源。
如果这句话现在还抽象,可以先记:
不要一上来学 allocator API。
先理解容器什么时候分配、什么时候构造元素、什么时候析构元素。7. 什么时候不要用 PMR
以下场景一般不需要:
普通业务代码
对象数量不大
没有明确性能瓶颈
团队不熟悉资源生命周期约束
代码可读性比微优化更重要PMR 是工具,不是现代 C++ 的必备仪式。
如果只是想避免手写 new/delete,应该先用:
std::vector
std::string
std::unique_ptr
std::shared_ptr不是先上 allocator。
8. 本篇总结
allocator/PMR 的位置是:
container
管理元素、size、capacity、构造析构
allocator / memory_resource
管理原始存储从哪里来、怎么归还最重要的判断是:
这个分配策略真的解决了当前问题吗?
memory_resource 是否比使用它的容器活得更久?
出了问题时,团队能不能读懂这层抽象?学到这里,你不需要马上写自定义 allocator。
但你应该能看懂:allocator 是把“内存从哪里来”从容器逻辑中抽出来。