内存池完整实现——C++20版

C++20 完整内存池实现

一、原理

1. 为什么需要内存池?

复制代码
普通 new/delete 的问题:
┌─────────────────────────────────────────────────────┐
│  new int  →  系统调用malloc  →  加锁  →  搜索空闲块  │
│           →  分割内存块      →  记录元数据           │
│           →  返回指针        (每次 ~100ns 以上)       │
└─────────────────────────────────────────────────────┘

内存池:
┌─────────────────────────────────────────────────────┐
│  pool.alloc()  →  从预分配数组取下一个槽  →  返回   │
│                   (几个指令,~5ns)                   │
└─────────────────────────────────────────────────────┘

2. 内存碎片问题

复制代码
频繁 new/delete 后的堆内存:
┌──┬────┬──┬──────┬───┬────┬──┐
│用│空闲│用│ 空闲 │用 │空闲│用│
└──┴────┴──┴──────┴───┴────┴──┘
  碎片化 → 大块申请失败 / cache miss 严重

内存池的连续内存:
┌──────────────────────────────┐
│ slot slot slot slot slot ... │  连续,cache友好
└──────────────────────────────┘

3. 内存池核心结构

复制代码
固定大小对象池 (Fixed-size Pool):
┌─────────────────────────────────────────────────────────┐
│  Block 0          Block 1          Block 2              │
│ ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │
│ │ Chunk[0]    │  │ Chunk[256]  │  │ Chunk[512]  │      │
│ │ Chunk[1]    │  │ Chunk[257]  │  │ ...         │      │
│ │ ...         │  │ ...         │  │             │      │
│ │ Chunk[255]  │  │ Chunk[511]  │  │             │      │
│ └─────────────┘  └─────────────┘  └─────────────┘      │
│       ↑                                                 │
│  FreeList: [3]→[7]→[1]→[255]→nullptr                   │
└─────────────────────────────────────────────────────────┘

可变大小内存池 (Variable-size Pool):
按大小分级,每级一个固定池
┌────────┬────────┬─────────┬──────────┬───────────┐
│ 8 byte │16 byte │ 32 byte │ 64 byte  │ 128 byte  │ ...
│  pool  │  pool  │  pool   │  pool    │   pool    │
└────────┴────────┴─────────┴──────────┴───────────┘

二、完整实现

文件结构

复制代码
memory_pool/
├── pool_utils.hpp        # 工具类
├── fixed_pool.hpp        # 固定大小对象池
├── variable_pool.hpp     # 可变大小内存池
├── thread_safe_pool.hpp  # 线程安全版本
├── stl_allocator.hpp     # STL分配器适配
└── memory_pool.hpp       # 统一入口

pool_utils.hpp

C++ 复制代码
#pragma once
#include <cstddef>
#include <cstdint>
#include <cassert>
#include <bit>
#include <concepts>
#include <type_traits>
#include <new>

namespace mp {

// ── 对齐工具 ────────────────────────────────────────────────
inline constexpr std::size_t align_up(std::size_t n, std::size_t align) noexcept {
    assert(std::has_single_bit(align) && "align must be power of 2");
    return (n + align - 1) & ~(align - 1);
}

inline constexpr bool is_aligned(const void* p, std::size_t align) noexcept {
    return (reinterpret_cast<std::uintptr_t>(p) & (align - 1)) == 0;
}

// ── 概念约束 ────────────────────────────────────────────────
template<typename T>
concept Poolable = std::is_destructible_v<T>;

// ── 统计信息 ────────────────────────────────────────────────
struct PoolStats {
    std::size_t total_bytes    = 0;  // 总分配内存
    std::size_t used_bytes     = 0;  // 已使用
    std::size_t alloc_count    = 0;  // 分配次数
    std::size_t dealloc_count  = 0;  // 释放次数
    std::size_t block_count    = 0;  // 内存块数量
    std::size_t free_slots     = 0;  // 空闲槽数量

    [[nodiscard]] double usage_rate() const noexcept {
        return total_bytes ? double(used_bytes) / double(total_bytes) : 0.0;
    }
};

// ── 不可拷贝基类 ─────────────────────────────────────────────
struct NonCopyable {
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
    NonCopyable(NonCopyable&&) = default;
    NonCopyable& operator=(NonCopyable&&) = default;
};

} // namespace mp

fixed_pool.hpp

C++ 复制代码
#pragma once
#include "pool_utils.hpp"
#include <memory>
#include <vector>
#include <span>
#include <format>
#include <stdexcept>
#include <utility>

namespace mp {

// ════════════════════════════════════════════════════════════
//  FixedPool<T, ChunkPerBlock>
//  固定对象大小内存池
//  原理:
//    1. 预分配大块内存(Block),切分成等大小的Chunk
//    2. 空闲Chunk用单向链表串联(FreeList)
//    3. alloc: 从FreeList头部取走一个节点  O(1)
//    4. dealloc: 归还节点到FreeList头部   O(1)
// ════════════════════════════════════════════════════════════
template<Poolable T, std::size_t ChunksPerBlock = 256>
class FixedPool : NonCopyable {
public:
    // ── 类型 ─────────────────────────────────────────────
    using value_type = T;
    static constexpr std::size_t chunk_size  =
        align_up(sizeof(T), alignof(T));
    static constexpr std::size_t block_size  = chunk_size * ChunksPerBlock;

private:
    // ── FreeList 节点 ─────────────────────────────────────
    // 技巧:对象未使用时,其内存复用为链表节点指针
    // 这样不需要额外空间存储链表
    struct FreeNode {
        FreeNode* next = nullptr;
    };
    static_assert(chunk_size >= sizeof(FreeNode),
        "T is too small to hold a FreeNode pointer");

    // ── Block:一次向OS申请的大块内存 ─────────────────────
    struct Block {
        // 用 aligned_storage 保证内存对齐
        alignas(alignof(T))
        std::byte data[block_size];

        explicit Block() = default;
    };

    // ── 成员 ──────────────────────────────────────────────
    std::vector<std::unique_ptr<Block>> blocks_;  // 所有Block
    FreeNode*  free_head_ = nullptr;              // FreeList头
    std::size_t free_count_ = 0;                 // 空闲数量
    std::size_t total_count_ = 0;                // 总槽数量
    PoolStats  stats_;

public:
    // ── 构造/析构 ─────────────────────────────────────────
    explicit FixedPool(std::size_t initial_blocks = 1) {
        for (std::size_t i = 0; i < initial_blocks; ++i)
            grow();
    }

    ~FixedPool() {
        // Block由unique_ptr自动释放,但用户必须保证
        // 所有对象已析构(池不跟踪存活对象)
    }

    // ── 分配原始内存(不构造对象)────────────────────────
    [[nodiscard]] void* allocate_raw() {
        if (!free_head_) [[unlikely]] {
            grow();  // FreeList为空,扩容
        }

        // 从FreeList头部取出一个节点
        FreeNode* node = free_head_;
        free_head_ = node->next;
        --free_count_;

        // 统计
        ++stats_.alloc_count;
        stats_.used_bytes += chunk_size;

        return static_cast<void*>(node);
    }

    // ── 释放原始内存(不析构对象)────────────────────────
    void deallocate_raw(void* ptr) noexcept {
        if (!ptr) [[unlikely]] return;

        // 将内存块重新解释为FreeNode,插回FreeList头部
        FreeNode* node = static_cast<FreeNode*>(ptr);
        node->next = free_head_;
        free_head_ = node;
        ++free_count_;

        // 统计
        ++stats_.dealloc_count;
        stats_.used_bytes -= chunk_size;
    }

    // ── 构造对象(placement new)─────────────────────────
    template<typename... Args>
    [[nodiscard]] T* construct(Args&&... args) {
        void* mem = allocate_raw();
        try {
            return ::new(mem) T(std::forward<Args>(args)...);
        } catch (...) {
            deallocate_raw(mem);  // 构造失败归还内存
            throw;
        }
    }

    // ── 析构并释放对象 ────────────────────────────────────
    void destroy(T* ptr) noexcept {
        if (!ptr) return;
        ptr->~T();              // 显式析构
        deallocate_raw(ptr);    // 归还内存
    }

    // ── 统计 ──────────────────────────────────────────────
    [[nodiscard]] PoolStats stats() const noexcept {
        PoolStats s = stats_;
        s.total_bytes = total_count_ * chunk_size;
        s.free_slots  = free_count_;
        s.block_count = blocks_.size();
        return s;
    }

    [[nodiscard]] std::size_t capacity()  const noexcept { return total_count_; }
    [[nodiscard]] std::size_t free_count()const noexcept { return free_count_;  }
    [[nodiscard]] std::size_t used_count()const noexcept {
        return total_count_ - free_count_;
    }

    // ── 预热:预分配N个Block ──────────────────────────────
    void reserve(std::size_t object_count) {
        std::size_t needed_blocks =
            (object_count + ChunksPerBlock - 1) / ChunksPerBlock;
        while (blocks_.size() < needed_blocks)
            grow();
    }

    // ── 调试:打印内存布局 ────────────────────────────────
    void dump() const {
        auto s = stats();
        std::println("FixedPool<{}> blocks={} total={} used={} free={} "
                     "usage={:.1f}%",
            typeid(T).name(),
            s.block_count, total_count_,
            used_count(), free_count_,
            s.usage_rate() * 100.0);
    }

private:
    // ── 扩容:分配一个新Block ─────────────────────────────
    void grow() {
        auto block = std::make_unique<Block>();
        std::byte* base = block->data;

        // 将Block切分为ChunksPerBlock个Chunk
        // 并用头插法连接成FreeList
        //
        // 内存布局:
        // [Chunk0][Chunk1][Chunk2]...[Chunk255]
        //    ↓       ↓       ↓
        //  next → next → next → old_head
        //
        for (std::size_t i = ChunksPerBlock; i > 0; --i) {
            void* slot = base + (i - 1) * chunk_size;
            FreeNode* node = static_cast<FreeNode*>(slot);
            node->next = free_head_;
            free_head_ = node;
        }

        free_count_  += ChunksPerBlock;
        total_count_ += ChunksPerBlock;
        stats_.total_bytes += block_size;
        ++stats_.block_count;

        blocks_.push_back(std::move(block));
    }
};

// ════════════════════════════════════════════════════════════
//  FixedPool 的 RAII Handle
//  自动调用 pool.destroy()
// ════════════════════════════════════════════════════════════
template<Poolable T, std::size_t N = 256>
struct PoolPtr {
    using Pool = FixedPool<T, N>;

    Pool* pool = nullptr;
    T*    ptr  = nullptr;

    PoolPtr() = default;
    PoolPtr(Pool& p, T* raw) : pool(&p), ptr(raw) {}

    ~PoolPtr() { reset(); }

    // 移动语义
    PoolPtr(PoolPtr&& o) noexcept
        : pool(o.pool), ptr(o.ptr) {
        o.pool = nullptr; o.ptr = nullptr;
    }
    PoolPtr& operator=(PoolPtr&& o) noexcept {
        if (this != &o) { reset(); pool = o.pool; ptr = o.ptr;
                          o.pool = nullptr; o.ptr = nullptr; }
        return *this;
    }

    PoolPtr(const PoolPtr&) = delete;
    PoolPtr& operator=(const PoolPtr&) = delete;

    void reset() {
        if (pool && ptr) { pool->destroy(ptr); ptr = nullptr; }
    }

    T* operator->() const noexcept { return ptr; }
    T& operator*()  const noexcept { return *ptr; }
    explicit operator bool() const noexcept { return ptr != nullptr; }
};

// 工厂函数
template<Poolable T, std::size_t N = 256, typename... Args>
[[nodiscard]] PoolPtr<T, N> make_pool_ptr(FixedPool<T, N>& pool, Args&&... args) {
    return { pool, pool.construct(std::forward<Args>(args)...) };
}

} // namespace mp

variable_pool.hpp

C++ 复制代码
#pragma once
#include "pool_utils.hpp"
#include <array>
#include <memory>
#include <cstring>
#include <vector>

namespace mp {

// ════════════════════════════════════════════════════════════
//  LinearPool(线性分配器 / Arena)
//
//  原理:
//    维护一个大缓冲区和一个"水位线"指针
//    alloc: 将水位线向后移动size字节  O(1)
//    dealloc: 不支持单独释放,只能整体reset
//    适合:生命周期相同的一批临时对象(如每帧数据)
//
//  内存布局:
//  ┌────────────────────────────────────────────┐
//  │  已分配  │ padding │  已分配  │  剩余空间  │
//  └────────────────────────────────────────────┘
//  ↑begin                           ↑current    ↑end
// ════════════════════════════════════════════════════════════
class LinearPool : NonCopyable {
public:
    explicit LinearPool(std::size_t capacity)
        : buffer_(std::make_unique<std::byte[]>(capacity))
        , begin_(buffer_.get())
        , current_(buffer_.get())
        , end_(buffer_.get() + capacity)
        , capacity_(capacity)
    {}

    // ── 分配 ──────────────────────────────────────────────
    [[nodiscard]] void* allocate(std::size_t size,
                                  std::size_t alignment = alignof(std::max_align_t))
    {
        // 对齐当前指针
        void*       ptr  = current_;
        std::size_t room = static_cast<std::size_t>(end_ - current_);

        if (!std::align(alignment, size, ptr, room)) [[unlikely]] {
            throw std::bad_alloc{};
        }

        current_ = static_cast<std::byte*>(ptr) + size;
        ++stats_.alloc_count;
        stats_.used_bytes = static_cast<std::size_t>(current_ - begin_);
        return ptr;
    }

    // ── 构造对象 ──────────────────────────────────────────
    template<typename T, typename... Args>
    [[nodiscard]] T* construct(Args&&... args) {
        void* mem = allocate(sizeof(T), alignof(T));
        return ::new(mem) T(std::forward<Args>(args)...);
        // 注意:析构由用户调用或reset时统一处理
    }

    // ── 不支持单个释放,整体重置 ──────────────────────────
    void reset() noexcept {
        current_ = begin_;
        stats_.used_bytes = 0;
        ++stats_.dealloc_count;
    }

    // ── 保存/恢复水位线(嵌套作用域)────────────────────
    struct Marker {
        std::byte* saved;
    };
    [[nodiscard]] Marker save() const noexcept { return { current_ }; }
    void restore(Marker m) noexcept {
        assert(m.saved >= begin_ && m.saved <= end_);
        current_ = m.saved;
        stats_.used_bytes = static_cast<std::size_t>(current_ - begin_);
    }

    // ── 查询 ──────────────────────────────────────────────
    [[nodiscard]] std::size_t used()      const noexcept {
        return static_cast<std::size_t>(current_ - begin_);
    }
    [[nodiscard]] std::size_t remaining() const noexcept {
        return static_cast<std::size_t>(end_ - current_);
    }
    [[nodiscard]] std::size_t capacity()  const noexcept { return capacity_; }
    [[nodiscard]] PoolStats   stats()     const noexcept {
        PoolStats s = stats_;
        s.total_bytes = capacity_;
        return s;
    }

private:
    std::unique_ptr<std::byte[]> buffer_;
    std::byte* begin_;
    std::byte* current_;
    std::byte* end_;
    std::size_t capacity_;
    PoolStats  stats_;
};

// ════════════════════════════════════════════════════════════
//  BuddyPool(伙伴系统)
//
//  原理:
//    内存大小必须是2的幂次
//    分配时向上取整到最近的2^k
//    将内存递归对半分裂,直到正好合适
//    释放时与"伙伴"合并,避免碎片
//
//  分裂过程示例(总大小64,申请10字节→对齐到16):
//  Level 0: [         64          ]
//  Level 1: [    32    ][    32   ]
//  Level 2: [ 16 ][ 16 ][   32   ]
//           ^^^^
//           分配给用户
//
//  合并过程:
//  释放16 → 与右侧伙伴16合并→32 → 与右侧32合并→64
// ════════════════════════════════════════════════════════════
class BuddyPool : NonCopyable {
public:
    static constexpr std::size_t MIN_ORDER = 4;   // 最小块 2^4 = 16 字节
    static constexpr std::size_t MAX_ORDER = 24;  // 最大块 2^24 = 16MB

    explicit BuddyPool(std::size_t order = 20)    // 默认 1MB
        : order_(order)
        , size_(std::size_t(1) << order)
        , buffer_(std::make_unique<std::byte[]>(size_))
    {
        assert(order >= MIN_ORDER && order <= MAX_ORDER);
        // 初始化:最大空闲链表有一个完整块
        free_lists_.resize(order + 1);
        free_lists_[order].push_back(0);
        used_.resize(size_, false);
    }

    // ── 分配 ──────────────────────────────────────────────
    [[nodiscard]] void* allocate(std::size_t size) {
        if (size == 0) size = 1;
        std::size_t need_order = order_of(size);
        if (need_order > order_) [[unlikely]]
            throw std::bad_alloc{};

        // 从need_order开始向上找可用块
        std::size_t found_order = need_order;
        while (found_order <= order_ && free_lists_[found_order].empty())
            ++found_order;

        if (found_order > order_) [[unlikely]]
            throw std::bad_alloc{};

        // 取出块
        std::size_t block_offset = free_lists_[found_order].back();
        free_lists_[found_order].pop_back();

        // 反复对半分裂直到大小合适
        while (found_order > need_order) {
            --found_order;
            // 右半块作为伙伴放入free_list
            std::size_t buddy_offset = block_offset + (std::size_t(1) << found_order);
            free_lists_[found_order].push_back(buddy_offset);
        }

        // 记录分配信息(offset → order)
        alloc_map_[block_offset] = need_order;
        mark_used(block_offset, need_order, true);

        ++stats_.alloc_count;
        stats_.used_bytes += std::size_t(1) << need_order;

        return buffer_.get() + block_offset;
    }

    // ── 释放 ──────────────────────────────────────────────
    void deallocate(void* ptr) noexcept {
        if (!ptr) return;

        std::size_t offset = static_cast<std::byte*>(ptr) - buffer_.get();
        auto it = alloc_map_.find(offset);
        if (it == alloc_map_.end()) return;  // 非法指针

        std::size_t cur_order = it->second;
        alloc_map_.erase(it);
        mark_used(offset, cur_order, false);

        stats_.used_bytes -= std::size_t(1) << cur_order;
        ++stats_.dealloc_count;

        // 尝试与伙伴合并
        while (cur_order < order_) {
            // 伙伴的偏移量 = offset XOR 块大小
            std::size_t buddy_offset = offset ^ (std::size_t(1) << cur_order);

            // 检查伙伴是否空闲
            auto& fl = free_lists_[cur_order];
            auto buddy_it = std::ranges::find(fl, buddy_offset);
            if (buddy_it == fl.end()) break;  // 伙伴已被分配,停止合并

            // 移除伙伴
            fl.erase(buddy_it);

            // 合并:选较小的偏移作为新块起点
            offset = std::min(offset, buddy_offset);
            ++cur_order;
        }

        // 将合并后的块放入free_list
        free_lists_[cur_order].push_back(offset);
    }

    [[nodiscard]] PoolStats stats() const noexcept {
        PoolStats s = stats_;
        s.total_bytes = size_;
        s.block_count = alloc_map_.size();
        return s;
    }

private:
    // ── 计算需要的order ───────────────────────────────────
    static std::size_t order_of(std::size_t size) noexcept {
        std::size_t order = MIN_ORDER;
        std::size_t block = std::size_t(1) << order;
        while (block < size) { block <<= 1; ++order; }
        return order;
    }

    void mark_used(std::size_t offset, std::size_t order, bool val) noexcept {
        std::size_t sz = std::size_t(1) << order;
        for (std::size_t i = offset; i < offset + sz && i < size_; ++i)
            used_[i] = val;
    }

    std::size_t order_;
    std::size_t size_;
    std::unique_ptr<std::byte[]> buffer_;
    std::vector<std::vector<std::size_t>> free_lists_;  // 每级空闲链表
    std::unordered_map<std::size_t, std::size_t> alloc_map_; // offset→order
    std::vector<bool> used_;
    PoolStats stats_;
};

// ════════════════════════════════════════════════════════════
//  SlabPool(分级内存池)
//
//  原理:
//    对不同大小的申请,分发到对应大小级别的FixedPool
//    大小级别:8, 16, 32, 64, 128, 256, 512, 1024 字节
//    超过MaxSize的退回到全局 new
//
//  分级示意:
//  申请13字节 → 对齐到16 → 取 size_class[1] 的FixedPool
//  申请100字节→ 对齐到128→ 取 size_class[4] 的FixedPool
// ════════════════════════════════════════════════════════════
class SlabPool : NonCopyable {
public:
    static constexpr std::size_t CLASS_COUNT = 8;
    static constexpr std::size_t MAX_SIZE    = 1024;
    // 大小级别:8 16 32 64 128 256 512 1024
    static constexpr std::array<std::size_t, CLASS_COUNT> SIZE_CLASSES = {
        8, 16, 32, 64, 128, 256, 512, 1024
    };

    explicit SlabPool(std::size_t chunks_per_class = 512) {
        for (std::size_t i = 0; i < CLASS_COUNT; ++i) {
            slabs_[i] = std::make_unique<SlabClass>(
                SIZE_CLASSES[i], chunks_per_class);
        }
    }

    [[nodiscard]] void* allocate(std::size_t size) {
        if (size == 0) size = 1;

        // 超过最大级别,退回系统malloc
        if (size > MAX_SIZE) [[unlikely]] {
            ++large_alloc_count_;
            return ::operator new(size);
        }

        std::size_t idx = class_index(size);
        return slabs_[idx]->allocate();
    }

    void deallocate(void* ptr, std::size_t size) noexcept {
        if (!ptr) return;

        if (size > MAX_SIZE) [[unlikely]] {
            ::operator delete(ptr);
            return;
        }

        std::size_t idx = class_index(size);
        slabs_[idx]->deallocate(ptr);
    }

    // ── 统计 ──────────────────────────────────────────────
    void dump_stats() const {
        std::println("SlabPool stats (large_alloc={}):", large_alloc_count_);
        for (std::size_t i = 0; i < CLASS_COUNT; ++i) {
            auto [used, total] = slabs_[i]->counts();
            std::println("  [{:4d} bytes] used={:5d} total={:5d} usage={:.1f}%",
                SIZE_CLASSES[i], used, total,
                total ? double(used)/double(total)*100.0 : 0.0);
        }
    }

private:
    // ── 单个大小级别 ──────────────────────────────────────
    struct SlabClass {
        std::size_t  obj_size;
        std::size_t  chunks_per_block;
        struct FreeNode { FreeNode* next; };

        std::vector<std::unique_ptr<std::byte[]>> blocks;
        FreeNode*   free_head  = nullptr;
        std::size_t free_count = 0;
        std::size_t total      = 0;

        SlabClass(std::size_t sz, std::size_t cpb)
            : obj_size(std::max(sz, sizeof(FreeNode)))
            , chunks_per_block(cpb)
        {
            grow();
        }

        void* allocate() {
            if (!free_head) grow();
            FreeNode* node = free_head;
            free_head = node->next;
            --free_count;
            return static_cast<void*>(node);
        }

        void deallocate(void* ptr) noexcept {
            FreeNode* node = static_cast<FreeNode*>(ptr);
            node->next = free_head;
            free_head  = node;
            ++free_count;
        }

        void grow() {
            auto block = std::make_unique<std::byte[]>(
                obj_size * chunks_per_block);
            std::byte* base = block.get();
            for (std::size_t i = chunks_per_block; i > 0; --i) {
                FreeNode* node = reinterpret_cast<FreeNode*>(
                    base + (i-1) * obj_size);
                node->next = free_head;
                free_head  = node;
            }
            free_count += chunks_per_block;
            total      += chunks_per_block;
            blocks.push_back(std::move(block));
        }

        std::pair<std::size_t,std::size_t> counts() const noexcept {
            return { total - free_count, total };
        }
    };

    // ── 找到大小对应的级别下标 ────────────────────────────
    static std::size_t class_index(std::size_t size) noexcept {
        for (std::size_t i = 0; i < CLASS_COUNT; ++i)
            if (size <= SIZE_CLASSES[i]) return i;
        return CLASS_COUNT - 1;
    }

    std::array<std::unique_ptr<SlabClass>, CLASS_COUNT> slabs_;
    std::size_t large_alloc_count_ = 0;
};

} // namespace mp

thread_safe_pool.hpp

C++ 复制代码
#pragma once
#include "fixed_pool.hpp"
#include <mutex>
#include <atomic>
#include <thread>
#include <array>

namespace mp {

// ════════════════════════════════════════════════════════════
//  ThreadSafePool:粗粒度锁版本
//  适合竞争不激烈的场景
// ════════════════════════════════════════════════════════════
template<Poolable T, std::size_t ChunksPerBlock = 256>
class ThreadSafePool : NonCopyable {
    using Base = FixedPool<T, ChunksPerBlock>;
public:
    explicit ThreadSafePool(std::size_t initial = 1)
        : pool_(initial) {}

    template<typename... Args>
    [[nodiscard]] T* construct(Args&&... args) {
        std::scoped_lock lock(mutex_);
        return pool_.construct(std::forward<Args>(args)...);
    }

    void destroy(T* ptr) noexcept {
        std::scoped_lock lock(mutex_);
        pool_.destroy(ptr);
    }

    [[nodiscard]] PoolStats stats() const {
        std::scoped_lock lock(mutex_);
        return pool_.stats();
    }

private:
    mutable std::mutex mutex_;
    Base pool_;
};

// ════════════════════════════════════════════════════════════
//  LockFreePool:无锁版本(单生产者/单消费者)
//
//  原理:使用 atomic 指针实现无锁 FreeList
//  CAS(Compare-And-Swap)操作:
//    1. 读取当前head
//    2. 设置新节点的next = head
//    3. 原子地将head替换为新节点(若head未被修改)
//    4. 失败则重试
//
//  ABA问题:通过tagged pointer(低位存版本号)解决
// ════════════════════════════════════════════════════════════
template<Poolable T, std::size_t ChunksPerBlock = 256>
class LockFreePool : NonCopyable {
    static constexpr std::size_t chunk_size =
        align_up(sizeof(T), alignof(T));

    // ── Tagged Pointer 解决ABA问题 ────────────────────────
    // 将 64 位指针拆分:高48位=地址,低16位=版本号
    struct alignas(8) TaggedPtr {
        std::uintptr_t ptr : 48 = 0;
        std::uintptr_t tag : 16 = 0;

        TaggedPtr() = default;
        TaggedPtr(void* p, std::uint16_t t)
            : ptr(reinterpret_cast<std::uintptr_t>(p) >> 0), tag(t) {}

        void* as_ptr() const noexcept {
            return reinterpret_cast<void*>(ptr);
        }
    };

    struct FreeNode {
        std::atomic<TaggedPtr> next{};
    };

public:
    explicit LockFreePool(std::size_t initial_blocks = 2) {
        for (std::size_t i = 0; i < initial_blocks; ++i)
            grow();
    }

    // ── 无锁分配 ──────────────────────────────────────────
    [[nodiscard]] void* allocate_raw() {
        TaggedPtr old_head = head_.load(std::memory_order_acquire);

        while (true) {
            if (!old_head.as_ptr()) [[unlikely]] {
                grow_atomic();
                old_head = head_.load(std::memory_order_acquire);
                continue;
            }

            FreeNode* node = static_cast<FreeNode*>(old_head.as_ptr());
            TaggedPtr next = node->next.load(std::memory_order_relaxed);

            // CAS: 若head未变,则将head替换为next
            TaggedPtr new_head{ next.as_ptr(),
                static_cast<std::uint16_t>(old_head.tag + 1) };

            if (head_.compare_exchange_weak(
                    old_head, new_head,
                    std::memory_order_release,
                    std::memory_order_acquire))
            {
                ++alloc_count_;
                return node;
            }
            // CAS失败,重试(old_head已被更新)
        }
    }

    // ── 无锁释放 ──────────────────────────────────────────
    void deallocate_raw(void* ptr) noexcept {
        if (!ptr) return;
        FreeNode* node = static_cast<FreeNode*>(ptr);

        TaggedPtr old_head = head_.load(std::memory_order_acquire);
        while (true) {
            node->next.store(old_head, std::memory_order_relaxed);

            TaggedPtr new_head{ ptr,
                static_cast<std::uint16_t>(old_head.tag + 1) };

            if (head_.compare_exchange_weak(
                    old_head, new_head,
                    std::memory_order_release,
                    std::memory_order_acquire))
            {
                ++dealloc_count_;
                return;
            }
        }
    }

    template<typename... Args>
    [[nodiscard]] T* construct(Args&&... args) {
        void* mem = allocate_raw();
        try {
            return ::new(mem) T(std::forward<Args>(args)...);
        } catch (...) {
            deallocate_raw(mem);
            throw;
        }
    }

    void destroy(T* ptr) noexcept {
        if (!ptr) return;
        ptr->~T();
        deallocate_raw(ptr);
    }

    std::size_t alloc_count()   const noexcept { return alloc_count_.load();   }
    std::size_t dealloc_count() const noexcept { return dealloc_count_.load(); }

private:
    void grow() {
        auto block = std::make_unique<std::byte[]>(chunk_size * ChunksPerBlock);
        std::byte* base = block.get();

        for (std::size_t i = ChunksPerBlock; i > 0; --i) {
            void* slot = base + (i-1) * chunk_size;
            FreeNode* node = static_cast<FreeNode*>(slot);
            TaggedPtr cur_head = head_.load(std::memory_order_relaxed);
            node->next.store(cur_head, std::memory_order_relaxed);
            head_.store(TaggedPtr{slot, 0}, std::memory_order_release);
        }

        std::scoped_lock lock(block_mutex_);
        blocks_.push_back(std::move(block));
    }

    void grow_atomic() {
        // 只让一个线程扩容
        bool expected = false;
        if (growing_.compare_exchange_strong(expected, true)) {
            grow();
            growing_.store(false, std::memory_order_release);
        } else {
            // 其他线程等待扩容完成
            while (growing_.load(std::memory_order_acquire))
                std::this_thread::yield();
        }
    }

    std::atomic<TaggedPtr> head_{};
    std::atomic<bool>      growing_{ false };
    std::atomic<std::size_t> alloc_count_{ 0 };
    std::atomic<std::size_t> dealloc_count_{ 0 };

    std::mutex block_mutex_;
    std::vector<std::unique_ptr<std::byte[]>> blocks_;
};

// ════════════════════════════════════════════════════════════
//  ThreadLocalPool:线程本地池 + 全局回收站
//
//  原理:
//    每个线程有自己的本地FixedPool(无锁)
//    释放到其他线程的对象先进全局回收站
//    本地池耗尽时先从回收站取,再向OS申请
//    大幅减少跨线程竞争
// ════════════════════════════════════════════════════════════
template<Poolable T, std::size_t ChunksPerBlock = 256>
class ThreadLocalPool : NonCopyable {
    using LocalPool = FixedPool<T, ChunksPerBlock>;
public:
    template<typename... Args>
    [[nodiscard]] T* construct(Args&&... args) {
        return get_local().construct(std::forward<Args>(args)...);
    }

    void destroy(T* ptr) noexcept {
        if (!ptr) return;
        ptr->~T();
        get_local().deallocate_raw(ptr);
    }

private:
    LocalPool& get_local() {
        // thread_local 保证每线程唯一
        thread_local LocalPool local_pool{ 1 };
        return local_pool;
    }
};

} // namespace mp

stl_allocator.hpp

C++ 复制代码
#pragma once
#include "variable_pool.hpp"
#include <limits>

namespace mp {

// ════════════════════════════════════════════════════════════
//  PoolAllocator:符合C++标准的STL分配器
//  可用于 std::vector, std::list, std::map 等容器
// ════════════════════════════════════════════════════════════
template<typename T>
class PoolAllocator {
public:
    using value_type      = T;
    using size_type       = std::size_t;
    using difference_type = std::ptrdiff_t;
    using propagate_on_container_move_assignment = std::true_type;

    // 允许从其他类型的allocator转换
    template<typename U>
    struct rebind { using other = PoolAllocator<U>; };

    explicit PoolAllocator(SlabPool& pool) noexcept
        : pool_(&pool) {}

    template<typename U>
    PoolAllocator(const PoolAllocator<U>& other) noexcept
        : pool_(other.pool_) {}

    // ── 分配 ──────────────────────────────────────────────
    [[nodiscard]] T* allocate(std::size_t n) {
        if (n > max_size()) throw std::bad_array_new_length{};
        return static_cast<T*>(pool_->allocate(n * sizeof(T)));
    }

    // ── 释放 ──────────────────────────────────────────────
    void deallocate(T* ptr, std::size_t n) noexcept {
        pool_->deallocate(ptr, n * sizeof(T));
    }

    [[nodiscard]] size_type max_size() const noexcept {
        return std::numeric_limits<size_type>::max() / sizeof(T);
    }

    bool operator==(const PoolAllocator& o) const noexcept {
        return pool_ == o.pool_;
    }

    SlabPool* pool_;  // 指向共享池
};

// ── 便捷类型别名 ──────────────────────────────────────────
template<typename T>
using pool_vector = std::vector<T, PoolAllocator<T>>;

template<typename T>
using pool_list = std::list<T, PoolAllocator<T>>;

template<typename K, typename V>
using pool_map = std::map<K, V,
    std::less<K>,
    PoolAllocator<std::pair<const K, V>>>;

} // namespace mp

memory_pool.hpp(统一入口)

C++ 复制代码
#pragma once
#include "fixed_pool.hpp"
#include "variable_pool.hpp"
#include "thread_safe_pool.hpp"
#include "stl_allocator.hpp"

// 快捷宏:在类中声明使用对象池
#define DECLARE_POOL_ALLOC(ClassName)                              \
    static mp::FixedPool<ClassName>& pool() {                      \
        static mp::FixedPool<ClassName> _pool{4};                  \
        return _pool;                                              \
    }                                                              \
    static void* operator new(std::size_t) {                       \
        return pool().allocate_raw();                              \
    }                                                              \
    static void operator delete(void* p) noexcept {               \
        pool().deallocate_raw(p);                                  \
    }

三、完整使用示例

C++ 复制代码
#include "memory_pool.hpp"
#include <print>
#include <chrono>
#include <vector>
#include <thread>

using namespace mp;
using namespace std::chrono;

// ── 测试用结构体 ──────────────────────────────────────────
struct Particle {
    float x, y, z;
    float vx, vy, vz;
    float life;
    int   id;

    Particle(int id, float x, float y)
        : x(x), y(y), z(0), vx(0), vy(0), vz(0)
        , life(1.0f), id(id) {}
};

// 使用宏让类自动使用对象池
struct Node {
    int   val;
    Node* left  = nullptr;
    Node* right = nullptr;
    explicit Node(int v) : val(v) {}

    DECLARE_POOL_ALLOC(Node)
};

// ── 性能测试 ──────────────────────────────────────────────
void benchmark() {
    constexpr int N = 100'000;
    std::println("=== 性能测试 N={} ===", N);

    // 普通 new/delete
    {
        auto t0 = high_resolution_clock::now();
        std::vector<Particle*> ptrs(N);
        for (int i = 0; i < N; ++i)
            ptrs[i] = new Particle(i, float(i), float(i));
        for (auto p : ptrs) delete p;
        auto t1 = high_resolution_clock::now();
        std::println("new/delete:  {:>8} μs",
            duration_cast<microseconds>(t1-t0).count());
    }

    // FixedPool
    {
        FixedPool<Particle, 512> pool(4);
        auto t0 = high_resolution_clock::now();
        std::vector<Particle*> ptrs(N);
        for (int i = 0; i < N; ++i)
            ptrs[i] = pool.construct(i, float(i), float(i));
        for (auto p : ptrs) pool.destroy(p);
        auto t1 = high_resolution_clock::now();
        std::println("FixedPool:   {:>8} μs",
            duration_cast<microseconds>(t1-t0).count());
        pool.dump();
    }

    // LinearPool
    {
        LinearPool arena(sizeof(Particle) * N + 4096);
        auto t0 = high_resolution_clock::now();
        for (int i = 0; i < N; ++i)
            arena.construct<Particle>(i, float(i), float(i));
        arena.reset();
        auto t1 = high_resolution_clock::now();
        std::println("LinearPool:  {:>8} μs",
            duration_cast<microseconds>(t1-t0).count());
    }
}

// ── STL 容器使用示例 ──────────────────────────────────────
void stl_example() {
    std::println("\n=== STL 容器示例 ===");

    SlabPool slab;
    PoolAllocator<int> alloc(slab);

    // pool_vector 使用对象池分配内存
    pool_vector<int> vec(alloc);
    vec.reserve(1000);
    for (int i = 0; i < 1000; ++i) vec.push_back(i);
    std::println("pool_vector size={}", vec.size());

    slab.dump_stats();
}

// ── 线程安全示例 ──────────────────────────────────────────
void thread_example() {
    std::println("\n=== 多线程示例 ===");

    ThreadSafePool<Particle, 256> pool(4);

    auto worker = [&](int tid) {
        std::vector<Particle*> local;
        for (int i = 0; i < 1000; ++i)
            local.push_back(pool.construct(tid * 1000 + i, 0.f, 0.f));
        for (auto p : local)
            pool.destroy(p);
    };

    std::vector<std::thread> threads;
    for (int i = 0; i < 8; ++i)
        threads.emplace_back(worker, i);
    for (auto& t : threads) t.join();

    auto s = pool.stats();
    std::println("alloc={} dealloc={}", s.alloc_count, s.dealloc_count);
}

// ── LinearPool Marker 示例 ────────────────────────────────
void marker_example() {
    std::println("\n=== Marker 嵌套作用域 ===");

    LinearPool arena(1024 * 1024);  // 1MB

    // 外层分配
    auto* a = arena.construct<Particle>(1, 0.f, 0.f);
    std::println("after outer alloc: used={}", arena.used());

    // 保存水位线
    auto mark = arena.save();

    // 内层临时分配
    for (int i = 0; i < 100; ++i)
        arena.construct<Particle>(i, 0.f, 0.f);
    std::println("after inner alloc: used={}", arena.used());

    // 恢复水位线(内层全部释放)
    arena.restore(mark);
    std::println("after restore:     used={}", arena.used());
}

// ── DECLARE_POOL_ALLOC 宏示例 ─────────────────────────────
void macro_example() {
    std::println("\n=== 自动对象池 ===");

    // Node 的 new/delete 自动使用对象池
    Node* root  = new Node(1);
    root->left  = new Node(2);
    root->right = new Node(3);

    auto& pool = Node::pool();
    pool.dump();

    delete root->left;
    delete root->right;
    delete root;
}

int main() {
    benchmark();
    stl_example();
    thread_example();
    marker_example();
    macro_example();
    return 0;
}

四、原理总结

C++ 复制代码
┌─────────────────────────────────────────────────────────────┐
│                     内存池选型指南                           │
├──────────────────┬──────────────┬──────────────────────────┤
│     类型          │   适用场景    │        核心原理           │
├──────────────────┼──────────────┼──────────────────────────┤
│ FixedPool        │ 固定大小对象  │ FreeList链表 O(1)         │
│ LinearPool       │ 同生命周期批量│ 水位线指针前移 O(1)       │
│ BuddyPool        │ 可变大小/合并 │ 二分裂/伙伴合并           │
│ SlabPool         │ 通用可变大小  │ 多级FixedPool分发         │
│ ThreadSafePool   │ 多线程低竞争  │ mutex粗粒度锁             │
│ LockFreePool     │ 多线程高并发  │ CAS + Tagged Pointer      │
│ ThreadLocalPool  │ 多线程极低竞争│ thread_local无锁本地池    │
└──────────────────┴──────────────┴──────────────────────────┘
相关推荐
不吃土豆的马铃薯6 天前
5.SGI STL 二级空间配置器 _S_chunk_alloc核心函数解析
开发语言·c++·vscode·c·内存池
不吃土豆的马铃薯7 天前
4.SGI STL 二级空间配置器 allocate 与_S_refill 源码解析
c语言·开发语言·c++·dreamweaver·内存池
普通网友9 天前
记录我适配iOS26遇到的一些问题
c++20
前进吧-程序员10 天前
C++20/23 Ranges:从「迭代器对」到「可组合管道」
c++20
June`10 天前
高并发内存池如何实现
c++·tcmalloc·内存池
YYYing.11 天前
【C++项目之高并发内存池 (五)】一些小细节和性能优化及整体测试
c++·性能优化·高并发·内存池·基数树
YYYing.14 天前
【C++项目之高并发内存池 (四)】三层缓存的空间回收流程详解
c++·笔记·缓存·高并发·内存池
Shan120514 天前
实例分析:C++20的std::jthread
c++20
charlie11451419114 天前
基于开源项目的现代C++工程实践——OnceCallback 前置知识(下):C++20/23 高级特性
c++·开源·c++20