为何持有std::weak_ptr时std::pmr::unsynchronized_pool_resource不重用内存?
关于std::pmr内存池无法重用内存的疑问
我正在测试pmr(多态内存资源),编写了如下C++代码:
#include <print> #include <memory_resource> #include <memory> #include <vector> #include <string> #include <cassert> #include <unordered_map> struct LoggingMemoryResource : std::pmr::memory_resource { LoggingMemoryResource(std::string name, std::pmr::memory_resource* upstream) : name(std::move(name)), upstream(upstream) { assert(upstream); } private: std::string name; std::pmr::memory_resource* upstream; void* do_allocate(std::size_t bytes, std::size_t alignment) override { void* ptr = upstream->allocate(bytes, alignment); std::println("[{} (alloc)] Ptr: {} Size: {} Alignment: {}", name, ptr, bytes, alignment); return ptr; } void do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) override { std::println("[{} (dealloc)] Ptr: {} Size: {} Alignment: {}", name, ptr, bytes, alignment); upstream->deallocate(ptr, bytes, alignment); } bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override { return this == &other; } }; int main() { static constexpr std::size_t SIZE = 1024 * 1024; std::vector<std::byte> memory{SIZE}; std::pmr::monotonic_buffer_resource mbr{memory.data(), memory.size()}; std::pmr::unsynchronized_pool_resource upr{&mbr}; LoggingMemoryResource pool{"Pool", &upr}; std::pmr::polymorphic_allocator<int> allocator(&pool); std::unordered_map<int, std::weak_ptr<int>> map; for(int i = 0; i < 5; ++i) { auto sharedptr = std::allocate_shared<int>(allocator, i); std::println("value: {}", *sharedptr.get()); //map[i] = sharedptr; } return 0; }
测试现象:
- 注释掉
map[i] = sharedptr;时,日志显示同一内存地址被反复分配和释放,实现了内存重用; - 取消注释后,日志显示每次分配新的内存地址,直到循环结束才批量释放所有内存。
我的疑问:std::weak_ptr仅修改弱引用计数,并不持有强引用,为何此时std::pmr::unsynchronized_pool_resource无法重用内存?我哪里理解有误?
问题解析
核心原因在于**std::weak_ptr会持有控制块的引用**,而控制块正是通过你的pmr分配器分配的内存块之一。
具体细节:
调用
std::allocate_shared<int>(allocator, i)时,实际会分配两块内存:- 存储
int对象的内存块; - 存储引用计数(强、弱引用计数)及元数据的控制块。
这两块内存都通过你传入的pmr分配器allocator分配。
- 存储
注释掉
map[i] = sharedptr;时:- 每次循环结束,
sharedptr销毁,强引用计数降为0,int对象内存被释放;同时弱引用计数也为0,控制块内存也被释放。 - 两块内存立刻回到内存池,下一次循环可直接重用,因此会看到同一地址被反复分配。
- 每次循环结束,
取消注释
map[i] = sharedptr;时:weak_ptr绑定到sharedptr的控制块,使控制块的弱引用计数保持大于0。- 控制块内存只有在强、弱引用计数都降为0时才会释放,因此循环过程中,每次创建的控制块内存都不会被释放,一直被
map中的weak_ptr持有。 - 存储
int对象的内存块虽会在强引用计数降为0后释放,但unsynchronized_pool_resource是按内存块的大小和对齐规则管理池子的,控制块和int对象的内存块大小不同,释放的int对象内存块无法用来分配控制块,每次循环都需要新的控制块,因此只能不断分配新地址。
循环结束后,
map被销毁,所有weak_ptr销毁,控制块的弱引用计数降为0,所有控制块和之前释放的int对象内存块才会被批量释放,这就是循环结束后批量释放的原因。
总结:你误解了weak_ptr的影响范围——它不会持有对象本身,但会持有控制块,而控制块是通过pmr分配器分配的,只要控制块没被释放,对应的内存就不会回到池子里被重用。
内容的提问来源于stack exchange,提问作者Shout
相关产品推荐
相关产品推荐

