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无锁本地池 │
└──────────────────┴──────────────┴──────────────────────────┘