monotonic_buffer_resource 是 C++17 的单调内存资源,快因无释放、无碎片、仅指针偏移分配(O(1)),适合批量短生命周期对象;依赖预分配缓冲区,耗尽时 fallback 降速,非线程安全。

monotonic_buffer_resource 是什么,为什么它快
monotonic_buffer_resource 不是自己实现的内存池,而是 C++17 引入的标准库类(定义在 <memory_resource>),它提供一种“只增不减”的单调分配策略。快的关键在于:没有释放操作、无碎片管理、无链表遍历、所有分配都在一个连续缓冲区上用指针偏移完成——本质就是 bump pointer allocator。
它适合生命周期明确、批量申请后统一销毁的场景,比如函数内临时对象构造、解析一段 JSON 的中间节点、一次网络请求的全部小对象。
- 分配时间复杂度是
O(1),仅移动内部游标 - 不调用
malloc/new,除非初始缓冲区耗尽且设置了上游memory_resource - 析构时才一次性归还整个缓冲区(或转发给上游),无逐块回收开销
怎么用:从 buffer 到 resource 再到 allocator
不能直接 new monotonic_buffer_resource,它需要一个底层存储。最常见做法是栈上预分配 buffer,再绑定 resource:
char buffer[4096]; std::pmr::monotonic_buffer_resource pool(buffer, sizeof(buffer)); std::pmr::vector<int> vec(&pool); // 所有内存从此 pool 分配
注意三点:
立即学习“C++免费学习笔记(深入)”;
-
buffer生命周期必须长于pool,否则析构时访问野指针 - 如果 buffer 耗尽,
pool会 fallback 到默认资源(通常是new_delete_resource()),失去性能优势 —— 可显式传入备用 resource 控制行为 - 想用于标准容器,得用
std::pmr::版本(如std::pmr::string),普通std::string不接受memory_resource*
常见误用:释放、重用和线程安全
monotonic_buffer_resource 没有 deallocate() 接口,调用它会触发断言失败或未定义行为。试图手动释放单个对象(比如 pool.deallocate(ptr, size, align))是错的 —— 它只支持整体 reset 或析构时回收。
reset 后可重用,但要注意:
-
pool.release()会把游标打回起点,但**已构造的对象不会被析构** —— 这不是 GC,C++ 对象生命周期仍由作用域或显式destroy_at控制 - 若容器(如
std::pmr::vector)在release()前没清空,其内部指针会指向已被“重置”的区域,后续访问崩溃 - 它不是线程安全的:多个线程同时调用
allocate()会破坏游标。需外加锁,或为每线程独占一个 pool
性能关键:buffer 大小与 fallback 策略
速度损失几乎全来自 fallback 到全局堆。实测中,buffer 小于 2KB 时,频繁耗尽会导致 3–5 倍性能下降(尤其小对象高频分配)。
- 静态 buffer 推荐 ≥ 8KB;对不确定大小的场景,可用
std::vector<char>动态扩容,再用data()+size()构造 pool - 禁用 fallback:传入
nullptr作为上游 resource,耗尽时直接抛std::bad_alloc,便于压测定位 buffer 不足 - 避免混合大小分配:大量 8 字节 + 少量 4KB 块会快速撑爆 buffer,因 bump pointer 无法复用空洞 —— 这是单调性代价
真正超快的前提,是你的 workload 匹配它的假设:短生命周期、批量分配、极少或零释放。一旦开始 mix 长/短对象,或需要部分释放,就得换 unsynchronized_pool_resource 或自研 slab 分配器。


















