当我们频繁的申请内存(new、malloc) 和 释放内存(delete、free) 时,会产生内存碎片,而且申请和释放内存也会增加时间的消耗。内存池就是为了解决该问题,提高效率产生的。
这里使用分段分段连续的内存,来存储多次申请和释放的内存。
- 内存单元: 定义的内存最小单元
- 内存块: 一块连续的内存,可以存放多个内存单元
- 内存池: 用链表的形式管理多个内存块
1. 内存单元
下面是内存单元部分定义:
template<typename T>
class MenoryUnit
{
public:
T unitData;
MenoryUnit* pNext = nullptr;
};
- unitData 表示要存储的单元。
- pNext 指向下一个要赋值的单元。
2. 内存块
template<typename T>
class MemoryBlock
{
public:
void* data(void) {
return (MemoryBlock*)m_pData + 1;
}
// 创建内存块
static MemoryBlock* createMemory(MemoryBlock* pHead, int unitSize, int unitCount) {
int size = sizeof(MemoryBlock) + unitSize * unitCount;
MemoryBlock* pBlock = (MemoryBlock*)new char[size];
memset(pBlock, 0, size);
pBlock->m_pNext = pHead;
pBlock->m_pData = pBlock;
return pBlock;
}
// 销毁内存块
void destoryMemoryBlock(void) {
MemoryBlock* pBlock = this;
while (pBlock) {
MemoryBlock *tempBlock = pBlock;
pBlock = pBlock->m_pNext;
// 销毁内存块
delete[] tempBlock->m_pData;
}
}
friend class MemoryPool<T>;
private:
MemoryBlock* m_pNext = nullptr;
void *m_pData = nullptr;
};
内存块首部位置保存内存块的信息,这里仅保存了链表下一个节点指针 **m_pNext ** 和整个的数据指针 m_pData,剩下的数据为要存储内存单元的数据。
- 函数 createMemory 用于创建内存块,需要指定头内存块的指针,内存单元的大小和内存单元的个数。
- 函数 destoryMemoryBlock 释放内存块,释放时会释放掉以它为链首的后继链的内存块资源。
- 函数 data 用于获取内存单元块的首地址。
3. 内存池
template<typename T>
class MemoryPool
{
public:
MemoryPool() {}
~MemoryPool() {
// 销毁内存块
m_pBlockHead->destoryMemoryBlock();
}
public:
// 创建一个对象
T* createObject(void) {
if (m_pFreeUnit == nullptr)
{
// 创建内存块
m_pBlockHead = MemoryBlock<T>::createMemory(m_pBlockHead, sizeof(MenoryUnit<T>), m_nPerBlockUnitSize);
MenoryUnit<T>* pUnit = (MenoryUnit<T>*)m_pBlockHead->data();
m_pFreeUnit = pUnit;
// 为内存块中所有元素单元空间赋值下一个将要赋值的内容
for (int i = 0; i < m_nPerBlockUnitSize - 1; ++i) {
(pUnit + i)->pNext = pUnit + i + 1;
}
}
// 设置下一个被添加的元素的位置
MenoryUnit<T>* pDestObject = m_pFreeUnit;
m_pFreeUnit = m_pFreeUnit->pNext;
// 使用placement new 为内存区域赋值
::new(pDestObject) T();
return (T*)&(pDestObject->unitData);
}
// 销毁内存对象
void destoryObject(T* pUnit) {
if (m_pBlockHead == nullptr)
return;
// 循环遍历该元素
MemoryBlock<T> *pTempBlock = m_pBlockHead;
while (pTempBlock){
for (int i = 0; i < m_nPerBlockUnitSize; ++i) {
MenoryUnit<T> *pTempUnit = (MenoryUnit<T> *)pTempBlock->data() + i;
if (&(pTempUnit->unitData) == pUnit)
{
// 设置为下一个空位置
pTempUnit->pNext = m_pFreeUnit;
m_pFreeUnit = pTempUnit;
pUnit->~T();// 执行析构函数
return;
}
}
pTempBlock = m_pBlockHead->m_pNext;
}
}
private:
MemoryBlock<T>* m_pBlockHead = nullptr; // 内存块链表头指针
int m_nPerBlockUnitSize = 10; // 每个内存块中元素单元的个数
MenoryUnit<T> *m_pFreeUnit = nullptr; // 用来存放下一个添加元素的位置
};
内存池中 有创建元素的函数 createObject 和 删除元素的函数 destoryObject,这里删除不是释放内存,而是将该块内存的状态标记为未被使用的状态。
- m_pBlockHead: 存储内存块头指针
- m_nPerBlockUnitSize: 存储每个内存块中元素的个数
- m_pFreeUnit: 存储将要添加元素的位置地址。
在创建元素时,为了能够初始化类中的内容,使用了 placement new 关于 new和delete 可参考文章 C++中的new和delete
下面时测试代码:
class TestObject
{
public:
TestObject() {
std::cout << "Created TestObject_" << m_nCount << std::endl;
m_nNumber = m_nCount++;
}
~TestObject() {
std::cout << "Deleted TestObject_" << m_nNumber << std::endl;
}
void print(void) {
std::cout << "Object Info: m_nNumber " << m_nNumber << std::endl;
}
static int m_nCount;
int m_nNumber = 1;
};
int TestObject::m_nCount = 1;
int main(int argc, char** argv) {
MemoryPool<TestObject> memPool;
std::vector<TestObject*> vector;
// 创建对象
for (int i = 0; i < 20; ++i) {
TestObject* o = memPool.createObject();
o->print();
std::cout << "-------------------------" << std::endl;
vector.push_back(o);
}
// 销毁对象
for (auto iter = vector.begin(); iter != vector.end(); ++iter) {
memPool.destoryObject(*iter);
}
system("pause");
return 0;
}
测试结果打印输出:
Created TestObject_1
Object Info: m_nNumber 1
-------------------------
Created TestObject_2
Object Info: m_nNumber 2
-------------------------
Created TestObject_3
Object Info: m_nNumber 3
-------------------------
Created TestObject_4
Object Info: m_nNumber 4
-------------------------
Created TestObject_5
Object Info: m_nNumber 5
-------------------------
Created TestObject_6
Object Info: m_nNumber 6
-------------------------
Created TestObject_7
Object Info: m_nNumber 7
-------------------------
Created TestObject_8
Object Info: m_nNumber 8
-------------------------
Created TestObject_9
Object Info: m_nNumber 9
-------------------------
Created TestObject_10
Object Info: m_nNumber 10
-------------------------
Created TestObject_11
Object Info: m_nNumber 11
-------------------------
Created TestObject_12
Object Info: m_nNumber 12
-------------------------
Created TestObject_13
Object Info: m_nNumber 13
-------------------------
Created TestObject_14
Object Info: m_nNumber 14
-------------------------
Created TestObject_15
Object Info: m_nNumber 15
-------------------------
Created TestObject_16
Object Info: m_nNumber 16
-------------------------
Created TestObject_17
Object Info: m_nNumber 17
-------------------------
Created TestObject_18
Object Info: m_nNumber 18
-------------------------
Created TestObject_19
Object Info: m_nNumber 19
-------------------------
Created TestObject_20
Object Info: m_nNumber 20
-------------------------
Deleted TestObject_1
Deleted TestObject_2
Deleted TestObject_3
Deleted TestObject_4
Deleted TestObject_5
Deleted TestObject_6
Deleted TestObject_7
Deleted TestObject_8
Deleted TestObject_9
Deleted TestObject_10
Deleted TestObject_11
Deleted TestObject_12
Deleted TestObject_13
Deleted TestObject_14
Deleted TestObject_15
Deleted TestObject_16
Deleted TestObject_17
Deleted TestObject_18
Deleted TestObject_19
Deleted TestObject_20
作者:douzhq
个人主页:https://www.douzhq.cn
文章同步页:https://douzhq.cn/memory_pool_page/