如何用C++實現(xiàn)簡單的內(nèi)存池
內(nèi)存池(Memory Pool)是計算機編程中一種重要的內(nèi)存管理技術(shù),它預(yù)先分配一塊較大的內(nèi)存區(qū)域,并將其劃分為多個大小相等的內(nèi)存塊。這種技術(shù)旨在減少因頻繁申請和釋放小塊內(nèi)存而引發(fā)的性能開銷。下面,我們將結(jié)合代碼,一步步講解如何實現(xiàn)一個簡單的內(nèi)存池,并分析其工作原理。
一、內(nèi)存池的基本概念
內(nèi)存池是一種用于動態(tài)內(nèi)存分配的技術(shù),其核心思想是空間換時間。通過預(yù)先分配一大塊內(nèi)存,并將其劃分為多個小塊,內(nèi)存池能夠快速地為程序提供所需的內(nèi)存,而無需每次都向操作系統(tǒng)申請。這樣可以大大減少內(nèi)存分配和釋放的開銷,提高程序的運行效率。
二、內(nèi)存池的實現(xiàn)步驟
1. 定義內(nèi)存池類
首先,我們定義一個名為AdvancedMemoryPool的模板類,它接受一個類型參數(shù)T和一個默認(rèn)大小為100的整數(shù)參數(shù)PoolSize。這個類將用于管理內(nèi)存池的分配和回收。
template <typename T, size_t PoolSize = 100>
class AdvancedMemoryPool {
// ...
};
2. 初始化內(nèi)存池
在類的構(gòu)造函數(shù)中,我們調(diào)用expandPool函數(shù)來初始化內(nèi)存池。這個函數(shù)將分配一塊大小為PoolSize * sizeof(T)的內(nèi)存,并將其劃分為PoolSize個大小為sizeof(T)的內(nèi)存塊。這些內(nèi)存塊的地址被添加到freeChunks_列表中,表示它們是空閑的,可以被分配。
AdvancedMemoryPool() {
expandPool();
}
private:
void expandPool() {
char* newBlock = new char[sizeof(T) * PoolSize];
for (size_t i = 0; i < PoolSize; ++i) {
freeChunks_.push_back(reinterpret_cast<T*>(newBlock + i * sizeof(T)));
}
pool_.push_back(newBlock);
}
3. 分配內(nèi)存塊
alloc函數(shù)用于從內(nèi)存池中分配一個空閑的內(nèi)存塊。它首先檢查freeChunks_列表是否為空。如果為空,則調(diào)用expandPool函數(shù)來擴展內(nèi)存池。然后,它從freeChunks_列表中取出一個空閑的內(nèi)存塊,并將其從列表中移除。最后,返回這個內(nèi)存塊的地址。
T* alloc() {
std::lock_guard<std::mutex> lock(mutex_); // 確保線程安全
if (freeChunks_.empty()) {
expandPool();
}
T* ptr = freeChunks_.front();
freeChunks_.pop_front();
return ptr;
}
這里使用了std::lock_guard來確保在多線程環(huán)境下的線程安全。當(dāng)多個線程同時嘗試分配內(nèi)存時,std::mutex會確保同一時間只有一個線程能夠訪問內(nèi)存池。
4. 回收內(nèi)存塊
dealloc函數(shù)用于回收一個之前分配的內(nèi)存塊。它接受一個指向要回收的內(nèi)存塊的指針,并將這個指針添加到freeChunks_列表中,表示這個內(nèi)存塊現(xiàn)在是空閑的,可以被再次分配。
void dealloc(T* ptr) {
assert(ptr != nullptr); // 確保傳入的指針不為空
std::lock_guard<std::mutex> lock(mutex_); // 確保線程安全
freeChunks_.push_back(ptr);
}
同樣,這里也使用了std::lock_guard來確保線程安全。
5. 查詢內(nèi)存池狀態(tài)
我們還提供了兩個函數(shù)getFreeChunksCount和getUsedChunksCount來查詢內(nèi)存池的狀態(tài)。這兩個函數(shù)分別返回空閑和已使用的內(nèi)存塊數(shù)量。
size_t getFreeChunksCount() const {
std::lock_guard<std::mutex> lock(mutex_); // 確保線程安全
return freeChunks_.size();
}
size_t getUsedChunksCount() const {
return PoolSize - getFreeChunksCount();
}
三、使用內(nèi)存池
在主函數(shù)中,我們創(chuàng)建了一個AdvancedMemoryPool對象,并使用它來分配和回收內(nèi)存塊。通過調(diào)用alloc函數(shù),我們可以從內(nèi)存池中獲取一個空閑的內(nèi)存塊,并使用它來存儲數(shù)據(jù)。當(dāng)我們不再需要這個內(nèi)存塊時,可以調(diào)用dealloc函數(shù)將其回收回內(nèi)存池。
四、完整代碼
#include <iostream>
#include <list>
#include <mutex>
#include <cassert>
#include <cstdlib>
template <typename T, size_t PoolSize = 100>
class AdvancedMemoryPool {
public:
AdvancedMemoryPool() {
expandPool();
}
~AdvancedMemoryPool() {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& chunk : pool_) {
delete[] reinterpret_cast<char*>(chunk);
}
}
T* alloc() {
std::lock_guard<std::mutex> lock(mutex_);
if (freeChunks_.empty()) {
expandPool();
}
T* ptr = freeChunks_.front();
freeChunks_.pop_front();
return ptr;
}
void dealloc(T* ptr) {
assert(ptr != nullptr);
std::lock_guard<std::mutex> lock(mutex_);
freeChunks_.push_back(ptr);
}
size_t getFreeChunksCount() const {
std::lock_guard<std::mutex> lock(mutex_);
return freeChunks_.size();
}
size_t getUsedChunksCount() const {
return PoolSize - getFreeChunksCount();
}
private:
void expandPool() {
char* newBlock = new char[sizeof(T) * PoolSize];
for (size_t i = 0; i < PoolSize; ++i) {
freeChunks_.push_back(reinterpret_cast<T*>(newBlock + i * sizeof(T)));
}
pool_.push_back(newBlock);
}
mutable std::mutex mutex_;
std::list<T*> freeChunks_;
std::list<char*> pool_;
};
// 使用示例
struct ComplexObject {
int data[100];
// 假設(shè)這是一個復(fù)雜的對象,需要動態(tài)分配
};
int main() {
AdvancedMemoryPool<ComplexObject> pool;
ComplexObject* obj1 = pool.alloc();
ComplexObject* obj2 = pool.alloc();
std::cout << "Free chunks: " << pool.getFreeChunksCount() << std::endl;
std::cout << "Used chunks: " << pool.getUsedChunksCount() << std::endl;
pool.dealloc(obj1);
pool.dealloc(obj2);
std::cout << "Free chunks after deallocation: " << pool.getFreeChunksCount() << std::endl;
std::cout << "Used chunks after deallocation: " << pool.getUsedChunksCount() << std::endl;
return 0;
}