arena allocator 不能直接用 new/delete,因为二者每次调用均触发系统级内存分配,有锁、元数据开销和碎片风险;arena 采用批量申请+线性分配,自行管理指针偏移以绕过堆管理器。

arena allocator 为什么不能直接用 new/delete
因为 new 和 delete 每次调用都触发系统级内存分配(如 malloc 或 mmap),有锁、有元数据开销、有碎片风险。arena 的核心是「批量申请 + 线性分配」,自己管理内存块内的指针偏移,绕过堆管理器。
常见错误现象:写了类似 new char[size] 再手动 reinterpret_cast 分配对象,但没重载 operator new,导致构造函数不调用;或忘了对齐,std::string 等类型崩溃。
- 必须按最大对齐要求(通常是
alignof(std::max_align_t)或用alignof查具体类型)做地址对齐 - 不能直接在 arena 上 delete 对象——析构函数得手动调用,内存只在 arena 整体释放时归还
- arena 不支持单个对象回收,只支持「全部回滚」或「按顺序释放到某点」
如何实现一个线性增长的 arena(C++17)
关键不是写多 fancy 的类,而是守住两个接口:allocate(size, align) 返回裸指针,和 reset() 回退分配位置。底层用 std::vector<:byte></:byte> 或 mmap 管理大块内存。
示例核心逻辑:
struct arena {
std::vector<:byte> storage;
size_t offset = 0;
<pre class="brush:php;toolbar:false;">void* allocate(size_t size, size_t align = alignof(std::max_align_t)) {
size_t aligned = (offset + align - 1) & ~(align - 1);
if (aligned + size > storage.size()) {
storage.resize(std::max(storage.size() * 2, aligned + size));
}
void* ptr = storage.data() + aligned;
offset = aligned + size;
return ptr;
}
void reset() { offset = 0; }
};
-
storage必须用std::byte(C++17 起)或char,避免别名违规 - 对齐计算不能用
std::align(它只调整指针不保证空间足够),得自己算再检查容量 - 扩容策略影响性能:倍增太激进,固定增量易频繁 realloc;视场景选
std::max(4096, size * 2)更稳
怎么让 std::vector / std::string 用上 arena
靠自定义 Allocator。C++ 标准容器接受满足 Allocator 概念的类型,但注意:allocator 类型是容器模板参数的一部分,std::vector<int my_arena_alloc>></int> 和 std::vector<int></int> 是不同类型,不能混用。
典型错误:传入临时 arena 实例,或让 allocator 持有 arena 引用但 arena 生命周期更短,导致后续 allocate 访问野指针。
- arena 实例必须比所有使用它的容器活得更久(通常作为成员或全局 static)
- allocator 需实现
allocate/deallocate,但deallocate通常什么也不做(arena 不支持单对象释放) - 别忘了
construct/destroy:它们要调用 placement new 和显式析构,否则std::string内部 buffer 不会正确构造
arena allocator 在真实项目里容易被忽略的坑
最常出问题的不是分配逻辑,而是生命周期和线程安全。
比如多线程中每个线程一个 arena 很自然,但如果 arena 里分配了对象又传给其他线程(如放进全局队列),那个对象的内存会在本线程 arena reset() 后失效——而你根本收不到编译或运行时提示。
- arena 本身不是线程安全的:多个线程共用一个 arena 必须加锁,但加锁就抵消了大部分性能收益
- 调试困难:ASan/Valgrind 默认不检查 arena 内存,越界访问可能静默失败
- 异常安全需手动保障:如果构造函数抛异常,已分配但未构造完成的内存不会自动回退,得在 catch 里调用
reset()或记录 checkpoint
真正难的不是写出来,是想清楚哪块内存该归属哪个 arena、谁负责释放、异常路径下怎么清理。这些没法靠模板或宏自动推导。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











