内存池学习笔记(附C++完整实现)
一、内存池核心定义
内存池是程序初始化阶段提前向操作系统申请一块/多块连续的大块内存,按预设规则分割为不同规格的小块内存单元,供程序按需分配、回收与复用 的定制化内存管理机制。其核心目标是减少系统调用开销、降低内存碎片、提升高并发场景下的内存分配效率,是替代C++原生new/delete的高性能方案。
二、原生内存管理的缺点与内存池优势
1. 原生new/delete的核心问题
- 系统调用开销高 :底层最终调用
brk/mmap等系统调用,涉及用户态与内核态的频繁切换,频繁分配/释放小块内存时性能损耗极大;
- 内存碎片严重 :多次不规则分配释放后,产生外部碎片 (大量零散空闲块总和足够但无法分配连续大块)和内部碎片(分配块大于实际需求导致的空间浪费);
- 线程安全开销大:标准库分配器的全局锁机制,在多线程高并发场景下会产生严重的锁竞争,导致分配效率骤降;
- 分配效率不稳定:通用分配器需兼容所有内存大小请求,内部链表管理逻辑复杂,无法针对特定场景优化。
2. 内存池的核心优势
| 优势 |
具体说明 |
| 降低系统调用次数 |
一次性向OS申请大块内存,后续分配/回收均在用户态完成,无内核态切换开销 |
| 减少内存碎片 |
按固定规格分割内存块,按需匹配请求大小,避免零散空闲块产生;分级管理进一步降低内部碎片 |
| 提升分配效率 |
预设内存块结构,分配时直接从空闲链表取块,回收时快速挂回,逻辑简单且高效 |
| 内存复用性强 |
释放的内存块不立即归还给OS,而是加入空闲链表供后续复用,避免重复申请释放 |
| 定制化适配性 |
可针对业务场景定制内存块规格、池大小,适配高并发、嵌入式等特殊场景 |
三、内存池核心设计思路
1. 设计三要素
- 预分配:程序启动/初始化时,主动向操作系统申请连续的大块内存,作为内存池的基础缓冲区;
- 分块管理 :将大块缓冲区划分为固定大小的内存块(槽Slot) ,通过原子空闲链表管理所有空闲块,支持快速取块和挂块;
- 按需分配与回收:分配时根据请求大小匹配最合适的内存块,从空闲链表取出;回收时将内存块重新挂回空闲链表,标记为可用状态。
2. 经典内存池类型
| 内存池类型 |
核心特点 |
适用场景 |
本示例实现类型 |
| 定长内存池 |
所有内存块大小完全相同,管理逻辑最简单,效率最高 |
频繁分配相同大小对象的场景(如链表节点、网络数据包、固定结构对象) |
子池基础(64个定长子池) |
| 分级内存池 |
划分多个定长子池,每个子池对应一种固定规格的内存块,按需匹配请求 |
内存请求大小不固定的通用场景(如业务开发、高并发服务) |
整体架构(HashBucket分级) |
| 动态扩容内存池 |
初始分配固定大小内存,空闲块耗尽时自动向OS申请新块,扩展池容量 |
内存使用量波动较大的场景 |
本示例核心特性 |
3. 本示例核心设计架构
本示例采用HashBucket分级内存池架构核心设计如下:
- 分级子池:共64个定长内存池子池,槽大小从8B开始,以8B为步长递增(8B、16B、24B...512B),覆盖大部分小内存请求;
- Hash映射:根据内存请求大小,通过哈希计算快速匹配对应的子池(如申请20B内存,映射到24B的子池);
- 大内存兜底 :超过512B的内存请求,直接使用C++原生
operator new/operator delete,避免大内存块造成的内部碎片;
- 双空闲管理 :未被使用过的新槽通过指针偏移管理,已释放的旧槽通过原子空闲链表管理,优先复用旧槽;
- 无锁+细粒度锁 :空闲链表操作采用CAS无锁机制 ,内存块扩容采用细粒度互斥锁,兼顾效率与线程安全;
- 内存对齐:所有内存槽均按自身大小对齐,避免未对齐导致的性能损耗或硬件错误;
- 对象构造/析构分离 :通过定位new在分配的内存上构造对象,析构时先调用析构函数再回收内存,符合C++对象生命周期管理。
四、C++完整实现
1. 头文件(MemoryPool.h)
cpp
复制代码
#pragma once
#include <atomic>
#include <cassert>
#include <cstdint>
#include <mutex>
namespace MemoryPool
{
const int MEMORY_POOL_NUM = 64; // 分级内存池数量:64个定长子池
const int SLOT_BASE_SIZE = 8; // 槽基础大小:8字节,步长8字节
const int MAX_SLOT_SIZE = 512; // 最大槽大小:512字节,超过则用原生new/delete
/**
* 内存槽结构体:用于管理空闲链表和内存块
* 注意:sizeof(Slot) 并非实际槽大小,实际槽大小由对应MemoryPool的slotSize_指定
* 原子指针保证多线程下next指针操作的原子性,支持无锁CAS
*/
struct Slot
{
std::atomic<Slot*> next; // 原子指针:指向空闲链表下一个槽,无锁操作核心
};
/**
* 定长内存池子类:单个定长子池,管理一种固定大小的内存槽
* 核心特性:动态扩容、无锁空闲链表、内存对齐、细粒度锁扩容
*/
class MemoryPool
{
public:
// 构造函数:指定内存块大小(默认4096字节,页大小对齐)
explicit MemoryPool(size_t blockSize = 4096);
// 析构函数:释放所有向OS申请的内存块
~MemoryPool();
// 初始化:设置当前子池的槽大小,重置所有状态
void init(size_t slotSize);
// 分配内存:返回一个可用的内存槽指针
void* allocate();
// 回收内存:将释放的指针挂回空闲链表
void deallocate(void* ptr);
private:
// 扩容:向OS申请新的内存块,加入当前池并分割为槽
void allocateNewBlock();
// 内存对齐:计算指定指针到目标对齐数的填充大小
size_t padPointer(char* p, size_t align);
// 无锁入队:将槽推入空闲链表头部,CAS操作保证原子性
bool pushFreeList(Slot* slot);
// 无锁出队:从空闲链表头部取出一个槽,CAS操作保证原子性
Slot* popFreeList();
private:
size_t blockSize_; // 单个内存块大小(向OS申请的大块内存大小)
size_t slotSize_; // 当前池的内存槽大小(固定值,8的倍数)
Slot* firstBlock_; // 指向当前池管理的首个内存块(块链表头)
Slot* curSlot_; // 指向当前内存块中未被使用过的新槽
std::atomic<Slot*> freeList_; // 原子空闲链表头:管理已释放的可复用槽(无锁核心)
Slot* lastSlot_; // 当前内存块的最后一个有效槽标记:超过则需扩容
std::mutex mutexForBlock_; // 细粒度互斥锁:保证扩容操作的线程安全,避免重复申请内存块
};
/**
* 哈希桶分级内存池:对外提供统一接口,管理64个定长MemoryPool子池
* 单例模式:子池数组为静态局部变量,保证全局唯一且懒加载
* 核心功能:内存大小映射、子池获取、分配/回收封装、对象构造/析构
*/
class HashBucket
{
public:
// 初始化所有子池:设置每个子池的槽大小(8B~512B,步长8B)
static void initMemoryPool();
// 获取指定索引的子池:单例模式,返回静态子池数组的引用
static MemoryPool& getMemoryPool(int index);
/**
* 对外内存分配接口:根据大小自动匹配子池或使用原生new
* @param size 申请的内存大小
* @return 分配的内存指针,失败返回nullptr
*/
static void* useMemory(size_t size)
{
if (size <= 0) return nullptr;
// 大内存兜底:超过512B直接使用原生operator new
if (size > MAX_SLOT_SIZE) return operator new(size);
// 哈希映射:size/8向上取整,计算对应的子池索引((size+7)/8 -1)
int poolIndex = ((size + SLOT_BASE_SIZE - 1) / SLOT_BASE_SIZE) - 1;
return getMemoryPool(poolIndex).allocate();
}
/**
* 对外内存回收接口:根据大小自动匹配子池或使用原生delete
* @param ptr 待释放的内存指针
* @param size 内存块的实际大小(需与分配时一致)
*/
static void freeMemory(void* ptr, size_t size)
{
if (!ptr) return;
// 大内存兜底:超过512B直接使用原生operator delete
if (size > MAX_SLOT_SIZE)
{
operator delete(ptr);
return;
}
// 哈希映射:找到对应的子池进行回收
int poolIndex = ((size + SLOT_BASE_SIZE - 1) / SLOT_BASE_SIZE) - 1;
getMemoryPool(poolIndex).deallocate(ptr);
}
// 友元模板函数:对象构造与析构,封装定位new和析构函数调用
template<typename T, typename... Args>
friend T* newElement(Args&&... args);
template<typename T>
friend void deleteElement(T* p);
};
/**
* 模板函数:创建对象(内存分配+定位构造)
* 完美转发参数,支持任意构造函数,符合C++11及以上特性
* @param args 构造函数的参数包
* @return 构造完成的对象指针,内存分配失败返回nullptr
*/
template<typename T, typename... Args>
T* newElement(Args&&... args)
{
T* p = reinterpret_cast<T*>(HashBucket::useMemory(sizeof(T)));
if (p != nullptr)
{
// 定位new:在已分配的内存上构造对象,不重新申请内存
new(p) T(std::forward<Args>(args)...);
}
return p;
}
/**
* 模板函数:销毁对象(析构+内存回收)
* 先调用对象的析构函数,再回收内存,严格遵循C++对象生命周期
* @param p 待销毁的对象指针
*/
template<typename T>
void deleteElement(T* p)
{
if (p != nullptr)
{
// 显式调用析构函数:释放对象内部资源
p->~T();
// 回收内存:调用哈希桶的回收接口
HashBucket::freeMemory(reinterpret_cast<void*>(p), sizeof(T));
}
}
} // namespace MemoryPool
2. 源文件(MemoryPool.cpp)- 核心逻辑实现
cpp
复制代码
#include "MemoryPool.h"
namespace KamaMemoryPool
{
// MemoryPool构造函数:初始化所有成员变量,设置默认块大小
MemoryPool::MemoryPool(size_t blockSize)
: blockSize_(blockSize)
, slotSize_(0)
, firstBlock_(nullptr)
, curSlot_(nullptr)
, freeList_(nullptr)
, lastSlot_(nullptr)
{}
// MemoryPool析构函数:释放所有向OS申请的内存块(块链表遍历)
MemoryPool::~MemoryPool()
{
Slot* curBlock = firstBlock_;
while (curBlock != nullptr)
{
// 保存下一个块的指针,避免释放后悬空
Slot* nextBlock = curBlock->next.load(std::memory_order_relaxed);
// 原生operator delete:释放向OS申请的连续大块内存,无需调用析构
operator delete(reinterpret_cast<void*>(curBlock));
curBlock = nextBlock;
}
}
// 初始化子池:设置槽大小,重置所有状态为初始值
void MemoryPool::init(size_t slotSize)
{
assert(slotSize > 0 && "Slot size must be greater than 0");
slotSize_ = slotSize;
firstBlock_ = nullptr;
curSlot_ = nullptr;
freeList_.store(nullptr, std::memory_order_relaxed);
lastSlot_ = nullptr;
}
/**
* 内存分配核心逻辑:优先复用空闲链表的旧槽,再使用新槽,无槽则扩容
* 无锁操作空闲链表,细粒度锁保护扩容,兼顾效率与线程安全
* @return 可用的内存槽指针,永不返回nullptr(扩容保证)
*/
void* MemoryPool::allocate()
{
// 第一步:优先从无锁空闲链表取块,复用已释放的内存(无锁,效率最高)
Slot* slot = popFreeList();
if (slot != nullptr)
{
return slot;
}
// 第二步:无空闲旧槽,使用当前块的新槽,扩容操作加细粒度锁
Slot* tempSlot = nullptr;
{
// 加锁:仅保护扩容和curSlot_/lastSlot_操作,锁粒度极小
std::lock_guard<std::mutex> lock(mutexForBlock_);
// 检查当前块是否还有新槽,无则申请新块(扩容)
if (curSlot_ >= lastSlot_)
{
allocateNewBlock();
}
// 取出当前新槽,并将curSlot_后移(指向下一个新槽)
tempSlot = curSlot_;
// 指针偏移:Slot*类型,需按slotSize_计算偏移步长(slotSize_ / sizeof(Slot))
curSlot_ += slotSize_ / sizeof(Slot);
}
return tempSlot;
}
// 内存回收核心逻辑:将释放的指针转为Slot,挂回无锁空闲链表
void MemoryPool::deallocate(void* ptr)
{
if (ptr == nullptr) return;
// 类型转换:将void*转为Slot*,加入空闲链表
Slot* slot = reinterpret_cast<Slot*>(ptr);
pushFreeList(slot);
}
/**
* 扩容核心逻辑:向OS申请新块,加入块链表,分割为槽并做内存对齐
* 仅在当前块无新槽时调用,且被mutexForBlock_保护,线程安全
*/
void MemoryPool::allocateNewBlock()
{
// 向OS申请大块内存:大小为blockSize_,返回void*
void* newBlock = operator new(blockSize_);
Slot* newBlockSlot = reinterpret_cast<Slot*>(newBlock);
// 块链表头插法:将新块加入块链表,方便后续析构遍历
newBlockSlot->next.store(firstBlock_, std::memory_order_relaxed);
firstBlock_ = newBlockSlot;
// 计算内存块体起始地址:跳过块链表的next指针(sizeof(Slot*))
char* blockBody = reinterpret_cast<char*>(newBlock) + sizeof(Slot*);
// 内存对齐:计算块体起始地址到slotSize_对齐的填充大小
size_t paddingSize = padPointer(blockBody, slotSize_);
// 设置当前新槽起始地址:对齐后的块体地址
curSlot_ = reinterpret_cast<Slot*>(blockBody + paddingSize);
// 设置当前块最后一个有效槽标记:避免越界,预留slotSize_大小
lastSlot_ = reinterpret_cast<Slot*>(
reinterpret_cast<size_t>(newBlock) + blockSize_ - slotSize_ + 1
);
}
/**
* 内存对齐计算:计算指定指针到目标对齐数的填充大小
* @param p 待对齐的指针
* @param align 对齐数(当前池的slotSize_)
* @return 需填充的字节数,0表示已对齐
*/
size_t MemoryPool::padPointer(char* p, size_t align)
{
// 计算指针地址对对齐数的余数
size_t remainder = reinterpret_cast<size_t>(p) % align;
// 余数为0则已对齐,否则填充(align - remainder)字节
return (remainder == 0) ? 0 : (align - remainder);
}
/**
* 无锁入队:CAS操作将槽推入空闲链表头部
* 循环CAS直到成功,保证多线程下的原子性,无锁竞争
* @param slot 待入队的空闲槽
* @return 入队成功返回true(恒返回true,循环保证)
*/
bool MemoryPool::pushFreeList(Slot* slot)
{
while (true)
{
// 加载当前空闲链表头:宽松内存序,仅获取值
Slot* oldHead = freeList_.load(std::memory_order_relaxed);
// 设置新槽的next为当前头:准备头插
slot->next.store(oldHead, std::memory_order_relaxed);
// CAS原子操作:将freeList_从oldHead更新为slot
// 成功:返回true;失败:说明其他线程修改了freeList_,重试
if (freeList_.compare_exchange_weak(
oldHead, slot,
std::memory_order_release, // 成功:释放内存序,保证写操作可见性
std::memory_order_relaxed // 失败:宽松内存序,减少开销
))
{
return true;
}
}
}
/**
* 无锁出队:CAS操作从空闲链表头部取出一个槽
* 循环CAS直到成功或队列为空,保证多线程下的原子性
* @return 取出的空闲槽,队列为空返回nullptr
*/
Slot* MemoryPool::popFreeList()
{
while (true)
{
// 加载当前空闲链表头:获取内存序,保证读操作可见性
Slot* oldHead = freeList_.load(std::memory_order_acquire);
// 队列为空,直接返回nullptr
if (oldHead == nullptr)
{
return nullptr;
}
// 加载旧头的下一个节点:准备更新链表头
Slot* newHead = oldHead->next.load(std::memory_order_relaxed);
// CAS原子操作:将freeList_从oldHead更新为newHead
// 成功:返回oldHead(取出的槽);失败:重试
if (freeList_.compare_exchange_weak(
oldHead, newHead,
std::memory_order_acquire, // 成功:获取内存序
std::memory_order_relaxed // 失败:宽松内存序
))
{
return oldHead;
}
}
}
// 初始化所有子池:遍历64个子池,设置每个子池的槽大小(8B*(i+1))
void HashBucket::initMemoryPool()
{
for (int i = 0; i < MEMORY_POOL_NUM; ++i)
{
getMemoryPool(i).init(static_cast<size_t>((i + 1) * SLOT_BASE_SIZE));
}
}
/**
* 获取子池:单例模式,静态局部数组保证全局唯一且懒加载
* @param index 子池索引(0~63)
* @return 对应子池的引用,避免拷贝,保证全局唯一
*/
MemoryPool& HashBucket::getMemoryPool(int index)
{
assert(index >= 0 && index < MEMORY_POOL_NUM && "Pool index out of range");
// 静态局部数组:程序运行期仅初始化一次,单例且线程安全(C++11及以上)
static MemoryPool memoryPool[MEMORY_POOL_NUM];
return memoryPool[index];
}
} // namespace KamaMemoryPool
3. 测试示例(main.cpp)- 验证使用方式
cpp
复制代码
#include "MemoryPool.h"
#include <iostream>
#include <string>
#include <thread>
#include <vector>
using namespace KamaMemoryPool;
// 测试自定义类
class TestObject
{
private:
int a;
std::string b;
public:
TestObject(int a_, std::string b_) : a(a_), b(b_)
{
std::cout << "TestObject构造:a=" << a << ", b=" << b << std::endl;
}
~TestObject()
{
std::cout << "TestObject析构:a=" << a << ", b=" << b << std::endl;
}
void show()
{
std::cout << "TestObject:a=" << a << ", b=" << b << std::endl;
}
};
// 多线程测试函数:分配并释放大量对象,验证线程安全
void multiThreadTest(int threadId, int count)
{
std::vector<TestObject*> objs;
objs.reserve(count);
// 分配count个对象
for (int i = 0; i < count; ++i)
{
objs.push_back(newElement<TestObject>(threadId * 1000 + i, "test_" + std::to_string(threadId)));
}
// 访问对象
for (auto p : objs)
{
p->show();
}
// 释放对象
for (auto p : objs)
{
deleteElement(p);
}
std::cout << "线程" << threadId << ":完成" << count << "个对象的分配与释放" << std::endl;
}
int main()
{
// 第一步:初始化内存池(必须先调用,建议程序启动时调用)
HashBucket::initMemoryPool();
std::cout << "内存池初始化完成" << std::endl;
// 单线程测试
std::cout << "\n===== 单线程测试 =====" << std::endl;
TestObject* p1 = newElement<TestObject>(100, "single_thread");
if (p1)
{
p1->show();
deleteElement(p1);
}
// 多线程测试:4个线程,每个线程分配100个对象
std::cout << "\n===== 多线程测试 =====" << std::endl;
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i)
{
threads.emplace_back(multiThreadTest, i, 100);
}
// 等待所有线程完成
for (auto& t : threads)
{
t.join();
}
std::cout << "\n所有测试完成,内存池自动析构释放所有内存" << std::endl;
return 0;
}
五、核心实现细节与优化点解析
1. 无锁CAS操作(线程安全核心优化)
- 核心对象 :
std::atomic<Slot*> freeList_(原子空闲链表头)、Slot::next(原子链表节点);
- 核心操作 :
pushFreeList(CAS头插入队)、popFreeList(CAS头删除队);
- 优势:替代全局互斥锁,多线程操作空闲链表时无锁竞争,大幅提升高并发场景下的效率;
- 内存序优化 :使用
std::memory_order_relaxed(宽松)、std::memory_order_acquire(获取)、std::memory_order_release(释放),在保证内存可见性的前提下,减少内存屏障开销,提升性能。
2. 细粒度锁设计(扩容线程安全优化)
- 锁的作用范围 :仅保护
allocateNewBlock(扩容)和curSlot_/lastSlot_的指针偏移操作;
- 锁的粒度:极小,仅在当前块无新槽时才会加锁,且加锁后操作逻辑简单,耗时极短;
- 锁的类型 :
std::lock_guard(RAII机制,自动加锁解锁,避免死锁);
- 优势:避免全局大锁的严重竞争,兼顾扩容操作的线程安全和分配效率。
3. 双空闲管理机制(内存复用优化)
- 已释放槽 :通过
freeList_(原子空闲链表)管理,优先复用,减少内存碎片;
- 新槽 :通过
curSlot_/lastSlot_指针偏移管理,未被使用过,无需链表操作,效率更高;
- 分配优先级:先复用已释放槽,再使用新槽,最后扩容,最大化内存复用率,减少内存浪费。
4. 内存对齐处理(性能与兼容性优化)
- 对齐函数 :
padPointer,保证内存槽的起始地址为slotSize_的整数倍;
- 对齐必要性:硬件对对齐内存的访问速度更快,避免未对齐导致的性能损耗;部分硬件不支持未对齐访问,会直接触发错误;
- 对齐范围:新申请的内存块体均做对齐处理,所有分配的内存槽均满足对齐要求。
5. 分级哈希映射(通用场景适配优化)
- 分级设计:64个定长子池,覆盖8B~512B的内存请求,满足大部分小内存场景;
- 哈希计算 :
(size + 7) / 8 - 1,实现size/8的向上取整,快速匹配最小的可用子池,减少内部碎片;
- 大内存兜底 :超过512B的请求直接使用原生
new/delete,避免大内存块造成的严重内部碎片,兼顾通用性和效率。
6. 对象生命周期管理(C++特性适配)
- 定位new :
new(p) T(std::forward<Args>(args)...),在已分配的内存上构造对象,实现内存分配 与对象构造分离;
- 显式析构 :
p->~T(),在回收内存前显式调用析构函数,释放对象内部资源(如std::string的堆内存),避免内存泄漏;
- 完美转发 :
std::forward<Args>(args)...,支持任意构造函数参数,包括左值、右值,符合C++11及以上的现代特性;
- 模板封装 :
newElement/deleteElement模板函数,对外提供简洁的对象创建/销毁接口,屏蔽底层内存管理细节。
7. 动态扩容与块链表管理(内存利用率优化)
- 动态扩容:仅在当前块无新槽时才向OS申请新块,避免初始申请过大内存造成的资源浪费;
- 块链表:采用头插法管理所有向OS申请的内存块,析构时遍历链表即可释放所有内存,避免内存泄漏;
- 内存复用:释放的内存块不立即归还给OS,而是加入空闲链表供后续复用,减少系统调用次数。
六、技术点总结表
| 技术点 |
核心作用 |
本示例实现方式 |
| 分级内存池 |
适配通用内存请求,减少内部碎片 |
HashBucket管理64个定长子池,8B~512B步长8B |
| 无锁CAS操作 |
实现空闲链表的原子操作,提升高并发效率 |
std::atomic+compare_exchange_weak,push/pop无锁 |
| 细粒度锁 |
保证扩容线程安全,减少锁竞争 |
std::lock_guard保护allocateNewBlock,锁粒度极小 |
| 内存对齐 |
提升内存访问速度,保证硬件兼容性 |
padPointer函数计算填充大小,槽地址按slotSize_对齐 |
| 定位new |
实现内存分配与对象构造分离 |
new§ T(...)在已分配内存上构造对象 |
| 显式析构 |
释放对象内部资源,避免内存泄漏 |
回收内存前调用p->~T(),显式析构对象 |
| 哈希映射 |
快速匹配内存请求与子池,提升分配效率 |
(size+7)/8-1实现size/8向上取整,映射子池索引 |
| 双空闲管理 |
最大化内存复用,提升分配效率 |
空闲链表管理已释放槽,指针偏移管理新槽,优先复用旧槽 |
| 动态扩容 |
提高内存利用率,避免初始资源浪费 |
无新槽时调用allocateNewBlock向OS申请新块,头插法加入块链表 |
| 大内存兜底 |
兼顾通用性,避免大内存内部碎片 |
超过512B直接使用operator new/operator delete |
| RAII机制 |
保证锁的自动释放,避免死锁 |
std::lock_guard管理互斥锁,作用域结束自动解锁 |
| 完美转发 |
支持任意构造函数参数,适配现代C++ |
std::forward转发参数包,兼容左值/右值参数 |
| 单例模式 |
保证子池全局唯一,避免重复创建 |
静态局部MemoryPool数组,getMemoryPool返回引用 |
| 块链表管理 |
统一管理所有OS申请的内存,避免泄漏 |
头插法构建块链表,析构时遍历释放所有块 |
七、与经典内存池实现的对比
| 实现方案 |
核心特点 |
优势 |
劣势 |
本示例定位 |
| 基础定长内存池 |
单一定长块,管理逻辑简单 |
实现容易,效率极高 |
适用场景单一,有内部碎片 |
本示例的子池基础 |
| 本示例分级内存池 |
64个定长子池,无锁+细粒度锁 |
通用场景适配,高并发高效,线程安全 |
实现较复杂,有少量内部碎片 |
工业级基础版本 |
| tcmalloc(Google) |
多级缓存+TLS+页管理 |
极致高并发,低锁竞争,内存利用率高 |
实现极其复杂,体积较大 |
本示例的升级方向 |
| jemalloc(FreeBSD) |
分级池+多核优化+页分配 |
多核性能优异,碎片率低 |
配置复杂,适合服务端 |
高并发服务端适配参考 |
| 嵌入式静态内存池 |
无动态扩容,固定大小 |
资源占用可控,无内存溢出风险 |
灵活性差,需提前预估内存使用 |
嵌入式场景适配方向 |
总结
内存池是一种预分配内存并进行重复利用的技术,通过减少频繁的动态内存分配与释放操作,从而提高程序运行效率。内存池通常预先分配一块大的内存区域,将其划分为多个小块,每次需要分配内存时直接从这块区域中分配,而不是调用系统的动态分配函数(如new或malloc)。简单来说就是申请一块较大的内存块(不够继续申请),之后将这块内存的管理放在应用层执行,减少系统调用带来的开销。