内存池设计思路
- 内存块划分:将一大块内存划分成固定大小的小块,每个小块用于存储一个小对象。
- 空闲链表管理:使用一个链表来管理所有空闲的内存块,这样在分配内存时可以快速找到空闲块,在释放内存时可以将其重新加入链表。
- 内存分配策略:从空闲链表中取出一个内存块进行分配,当空闲链表为空时,向系统申请新的内存块并加入空闲链表。
- 内存释放策略:将释放的内存块重新加入空闲链表,而不是立即归还给系统,以便后续再次使用。
C++ 代码实现
#include <iostream>
#include <vector>
#include <mutex>
class MemoryPool {
public:
MemoryPool(size_t blockSize, size_t initialBlockCount)
: blockSize(blockSize), currentIndex(0) {
// 申请一大块内存
pool.resize(initialBlockCount * blockSize);
// 初始化空闲链表
for (size_t i = 0; i < initialBlockCount; ++i) {
freeList.push_back(&pool[i * blockSize]);
}
}
~MemoryPool() = default;
void* allocate() {
std::lock_guard<std::mutex> lock(mutex_);
if (freeList.empty()) {
// 空闲链表为空,申请新的内存块
size_t newBlockCount = 10; // 可调整
pool.resize(pool.size() + newBlockCount * blockSize);
for (size_t i = 0; i < newBlockCount; ++i) {
freeList.push_back(&pool[currentIndex]);
currentIndex += blockSize;
}
}
void* block = freeList.back();
freeList.pop_back();
return block;
}
void deallocate(void* ptr) {
std::lock_guard<std::mutex> lock(mutex_);
freeList.push_back(static_cast<char*>(ptr));
}
private:
size_t blockSize;
size_t currentIndex;
std::vector<char> pool;
std::vector<char*> freeList;
std::mutex mutex_;
};
性能优化
- 减少系统调用:尽量减少向系统申请和释放内存的次数,通过内存池的预分配和回收机制,提高内存使用效率。
- 内存对齐:确保内存块的分配满足对象的对齐要求,避免因内存对齐问题导致的性能损失。
- 批量分配与释放:在内存不足时,一次性申请多个内存块,减少频繁申请内存的开销;释放内存时,将多个连续的空闲块合并,提高内存利用率。
线程安全问题
- 互斥锁:在分配和释放内存的关键操作上加锁,如上述代码中使用
std::mutex
和std::lock_guard
来保证同一时间只有一个线程可以访问内存池,防止数据竞争。
- 无锁数据结构:可以考虑使用无锁数据结构(如无锁链表)来管理空闲内存块,进一步提高多线程环境下的性能,但实现相对复杂。
- 线程本地存储:对于每个线程,可以使用线程本地存储(TLS)来缓存一部分空闲内存块,减少线程间的竞争。