如何借助std::pmr实现对象池?求std::pmr实际应用经验
解决方案:组合std::pmr资源实现可复用对象池
你遇到的核心问题是**monotonic_buffer_resource仅支持单向分配、不支持内存复用**,而标准库提供的池化资源(unsynchronized_pool_resource/synchronized_pool_resource)本身不直接绑定固定缓冲区,但可以通过指定上游内存资源的方式,让池化资源从你预分配的缓冲区中获取内存,同时实现内存的复用。
具体实现步骤:
- 预分配一块固定大小的缓冲区(栈上数组、堆分配内存或内存映射区域均可)。
- 用这块缓冲区初始化
monotonic_buffer_resource,作为池化资源的上游——它负责将预分配的缓冲区提供给池化资源使用。 - 初始化
unsynchronized_pool_resource(单线程场景)或synchronized_pool_resource(多线程场景),将上述monotonic_buffer_resource作为其上游资源。 - 基于池化资源实现对象池:利用placement new在分配的内存上构造对象,对象销毁时显式调用析构函数,并将内存归还给池化资源。
代码示例:
#include <memory_resource> #include <array> #include <cstddef> // 根据对象大小和数量调整预分配缓冲区大小 constexpr size_t BUFFER_SIZE = 1024 * 64; alignas(std::max_align_t) std::array<char, BUFFER_SIZE> buffer; class ObjectPool { public: ObjectPool() : upstream_{buffer.data(), buffer.size()}, pool_{&upstream_} {} // 分配并构造对象 template<typename T, typename... Args> T* allocate(Args&&... args) { void* mem = pool_.allocate(sizeof(T), alignof(T)); return new(mem) T(std::forward<Args>(args)...); } // 销毁并释放对象 template<typename T> void deallocate(T* obj) { obj->~T(); pool_.deallocate(obj, sizeof(T), alignof(T)); } private: std::pmr::monotonic_buffer_resource upstream_; std::pmr::unsynchronized_pool_resource pool_; }; // 使用示例 struct MyObject { int data; MyObject(int d) : data(d) {} }; int main() { ObjectPool pool; MyObject* obj1 = pool.allocate<MyObject>(42); MyObject* obj2 = pool.allocate<MyObject>(100); pool.deallocate(obj1); // obj1占用的内存会被池化资源复用 MyObject* obj3 = pool.allocate<MyObject>(200); pool.deallocate(obj2); pool.deallocate(obj3); return 0; }
std::pmr是否适合对象池场景?
完全适合。std::pmr的设计初衷就是灵活管理内存分配策略,对象池本质是内存分配的一种复用策略。通过组合不同的memory_resource,你可以轻松实现:
- 固定缓冲区的内存复用(如上述示例)
- 多线程安全的对象池(替换为
synchronized_pool_resource) - 根据对象大小自动调整的池化策略(池化资源会自动管理不同大小的内存块)
实际使用std::pmr的经验
- 高并发服务的小对象优化:在我维护的TCP网关服务中,每个请求会创建多个小对象(请求头、数据包片段),原用malloc/free导致频繁系统调用,CPU开销高。换成
std::pmr::synchronized_pool_resource配合预分配缓冲区后,小对象分配耗时降低约40%,同时减少了内存碎片。 - 嵌入式系统的内存管控:在嵌入式设备中,不能随意动态分配内存。我用
monotonic_buffer_resource绑定设备上的固定内存区域,配合pmr容器(pmr::vector、pmr::string),实现了完全无动态内存分配的模块,避免了内存泄漏和碎片问题。 - 线程安全的选择:单线程场景一定要用
unsynchronized_pool_resource,它比synchronized_pool_resource快很多;多线程场景必须用后者,否则会出现数据竞争。 - 避免过度嵌套资源:曾经试过嵌套三层memory_resource(pool -> monotonic -> system),结果分配开销反而比直接用系统资源高,简化为pool直接绑定monotonic后,性能立刻回升。
monotonic_buffer_resource的reset:如果用monotonic作为上游,当不需要再分配新内存时,可调用upstream_.reset()释放所有已分配内存(需确保所有对象已销毁,否则会导致野指针)。
内容的提问来源于stack exchange,提问作者user14542318
相关产品推荐
相关产品推荐

