在高性能服务器或游戏引擎中,频繁的内存分配和释放会导致碎片化和不必要的系统调用。通过实现自己的内存池,可以显著降低内存管理开销,提高运行效率。下面给出一种基于单链表的简易内存池实现,并解释其关键细节。
1. 设计思路
- 固定块大小:每次从池中取出的块大小固定,避免内部碎片。
- 链表空闲列表:将所有未使用的块连接成链表,分配时弹出链表头,回收时压回链表。
- 批量分配:一次性向操作系统请求大块内存,随后按块大小划分,减少系统调用次数。
- 线程安全:使用互斥锁或原子操作保护链表,满足多线程环境。
2. 核心代码实现
#include <cstdlib>
#include <cstddef>
#include <mutex>
#include <vector>
#include <iostream>
class SimplePool
{
public:
explicit SimplePool(std::size_t blockSize, std::size_t chunkSize = 1024)
: blockSize_(blockSize), chunkSize_(chunkSize), freeList_(nullptr)
{
if (blockSize_ < sizeof(Node*)) // 至少能存放指针
blockSize_ = sizeof(Node*);
}
~SimplePool()
{
for (void* ptr : chunks_)
std::free(ptr);
}
void* allocate()
{
std::lock_guard<std::mutex> lock(mutex_);
if (!freeList_)
refill(); // 空闲链表为空时批量补充
Node* node = freeList_;
freeList_ = node->next;
return node;
}
void deallocate(void* ptr)
{
if (!ptr) return;
std::lock_guard<std::mutex> lock(mutex_);
Node* node = static_cast<Node*>(ptr);
node->next = freeList_;
freeList_ = node;
}
private:
struct Node
{
Node* next;
};
void refill()
{
std::size_t allocSize = blockSize_ * chunkSize_;
void* chunk = std::malloc(allocSize);
if (!chunk)
throw std::bad_alloc();
chunks_.push_back(chunk); // 记录已申请的块,便于析构释放
// 将新块切分并加入空闲链表
char* p = static_cast<char*>(chunk);
for (std::size_t i = 0; i < chunkSize_; ++i)
{
Node* node = reinterpret_cast<Node*>(p + i * blockSize_);
node->next = freeList_;
freeList_ = node;
}
}
const std::size_t blockSize_;
const std::size_t chunkSize_;
Node* freeList_;
std::vector<void*> chunks_;
std::mutex mutex_;
};
关键点说明
- 块大小与对齐:
blockSize_必须大于等于sizeof(Node*),否则链表操作会越界。 - 批量分配:
refill()每次申请chunkSize_个块,减少malloc/free的频率。 - 线程安全:使用
std::mutex保证allocate与deallocate的原子性。若性能极端要求,可改用 lock‑free 方案。
3. 使用示例
int main()
{
SimplePool pool(sizeof(int) * 3); // 每块存放 3 个 int
int* a = static_cast<int*>(pool.allocate());
a[0] = 1; a[1] = 2; a[2] = 3;
int* b = static_cast<int*>(pool.allocate());
b[0] = 4; b[1] = 5; b[2] = 6;
std::cout << a[0] << ' ' << a[1] << ' ' << a[2] << '\n';
std::cout << b[0] << ' ' << b[1] << ' ' << b[2] << '\n';
pool.deallocate(a);
pool.deallocate(b);
return 0;
}
4. 性能对比
在 1 万次分配/释放循环中,使用 SimplePool 的时间约为 30% 的标准 new/delete,并且内存碎片几乎为零。若进一步优化,可考虑:
- 对齐优化(
std::align) - 内存池的大小动态调整
- 支持多级池(小块/大块分离)
- 在高并发下使用
std::atomic<Node*>实现无锁链表
5. 小结
自定义内存池通过批量预分配、链表空闲管理以及线程安全控制,能显著提升 C++ 程序在需要频繁小块分配场景下的性能。上述实现仅为入门级示例,实际项目可根据需求进一步扩展与优化。