You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何持有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分配器分配的内存块之一。

具体细节:

  1. 调用std::allocate_shared<int>(allocator, i)时,实际会分配两块内存:

    • 存储int对象的内存块;
    • 存储引用计数(强、弱引用计数)及元数据的控制块。
      这两块内存都通过你传入的pmr分配器allocator分配。
  2. 注释掉map[i] = sharedptr;时:

    • 每次循环结束,sharedptr销毁,强引用计数降为0,int对象内存被释放;同时弱引用计数也为0,控制块内存也被释放。
    • 两块内存立刻回到内存池,下一次循环可直接重用,因此会看到同一地址被反复分配。
  3. 取消注释map[i] = sharedptr;时:

    • weak_ptr绑定到sharedptr的控制块,使控制块的弱引用计数保持大于0。
    • 控制块内存只有在强、弱引用计数都降为0时才会释放,因此循环过程中,每次创建的控制块内存都不会被释放,一直被map中的weak_ptr持有。
    • 存储int对象的内存块虽会在强引用计数降为0后释放,但unsynchronized_pool_resource是按内存块的大小和对齐规则管理池子的,控制块和int对象的内存块大小不同,释放的int对象内存块无法用来分配控制块,每次循环都需要新的控制块,因此只能不断分配新地址。
  4. 循环结束后,map被销毁,所有weak_ptr销毁,控制块的弱引用计数降为0,所有控制块和之前释放的int对象内存块才会被批量释放,这就是循环结束后批量释放的原因。

总结:你误解了weak_ptr的影响范围——它不会持有对象本身,但会持有控制块,而控制块是通过pmr分配器分配的,只要控制块没被释放,对应的内存就不会回到池子里被重用。

内容的提问来源于stack exchange,提问作者Shout

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:35:00