原理
还回来的内存用链表串联起来,称为自由链表
内存块自身进行链接,前四个字节存下一个的地址
结构
template<class T>
class ObjectPool
{
public:
T* New()
{
}
private:
char* _memory = nullptr; //方便切割
void* _freeList = nullptr;
};
第一步:先给_memory搞一块大内存
T* New()
{
if (_remainBytes <sizeof(T)) //剩余的内存不够分配一个T的大小,重开一个大空间
{
_remainBytes = 128 * 1024;
_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byte
if (_memory == nullptr)
{
throw bad_alloc();
}
}
T* obj = (T*)_memory;
_memory += sizeof(T);
_remainBytes -= sizeof(T);
return obj;
}
考虑对象的还回
前四/八个字节空间用来指向
采用头插的方式
new从freeList中优先切出
T* New()
{
T* obj = nullptr;
//优先利用freeList中的
if (_freeList)
{
void* next = *((void**)_freeList);
obj =(T*)_freeList; //obj指向第一块
_freeList = next;
return obj;
}
else
{
if (_remainBytes < sizeof(T)) //剩余的内存不够分配一个T的大小,重开一个大空间
{
_remainBytes = 128 * 1024;
_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byte
if (_memory == nullptr)
{
throw bad_alloc();
}
}
obj = (T*)_memory;
_memory += sizeof(T);
_remainBytes -= sizeof(T);
return obj;
}
}
平台冲突问题,申请值小于一个指针大小(64位平台,指针大小为8byte),则给一个指针。并且保证自由链表在链接时,空间至少能存进下一个对象的地址。
加上obj初始化,显示调用析构函数清理对象(T)
#pragma once
#include <iostream>
using std::cout;
using std::endl;
using std::bad_alloc;
//定长内存池
//template<size_t N>
//class ObjectPool
//{};
template<class T>
class ObjectPool
{
public:
T* New()
{
T* obj = nullptr;
//优先利用freeList中的
if (_freeList)
{
void* next = *((void**)_freeList);
obj =(T*)_freeList; //obj指向第一块
_freeList = next;
}
else
{
if (_remainBytes < sizeof(T)) //剩余的内存不够分配一个T的大小,重开一个大空间
{
_remainBytes = 128 * 1024;
_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byte
if (_memory == nullptr)
{
throw bad_alloc();
}
}
obj = (T*)_memory;
size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
_memory += objSize;
_remainBytes -= objSize;
}
//定位new,显示调用T的构造函数初始化
new(obj)T;
return obj;
}
void Delete(T* obj)
{
//显示调用析构函数清理对象(T)
obj->~T();
//头插
*(void**)obj = _freeList;
_freeList = obj;
}
private:
char* _memory = nullptr; //方便切割 指向大块内存的指针
size_t _remainBytes = 0; //大块内存在切分过程中剩余字节数
void* _freeList = nullptr;//还回来的内存链接到的自由链表的头指针
};
测试性能
用数结构分别对比vector容器下(malloc)std::vector<TreeNode*>和此内存池下ObjectPool的运行时间
ConcurrentMemoryPoll.cpp
// ConcurrentMemoryPool.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "ObjectPool.h"
int main()
{
TestObjectPool();
return 0;
}
ObjectPool.h
#pragma once
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::bad_alloc;
//定长内存池
//template<size_t N>
//class ObjectPool
//{};
template<class T>
class ObjectPool
{
public:
T* New()
{
T* obj = nullptr;
//优先利用freeList中的
if (_freeList)
{
void* next = *((void**)_freeList);
obj =(T*)_freeList; //obj指向第一块
_freeList = next;
}
else
{
if (_remainBytes < sizeof(T)) //剩余的内存不够分配一个T的大小,重开一个大空间
{
_remainBytes = 128 * 1024;
_memory = (char*)malloc(_remainBytes); //分配初始的大内存128byte
if (_memory == nullptr)
{
throw bad_alloc();
}
}
obj = (T*)_memory;
size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);
_memory += objSize;
_remainBytes -= objSize;
}
//定位new,显示调用T的构造函数初始化
new(obj)T;
return obj;
}
void Delete(T* obj)
{
//显示调用析构函数清理对象(T)
obj->~T();
//头插
*(void**)obj = _freeList;
_freeList = obj;
}
private:
char* _memory = nullptr; //方便切割 指向大块内存的指针
size_t _remainBytes = 0; //大块内存在切分过程中剩余字节数
void* _freeList = nullptr;//还回来的内存链接到的自由链表的头指针
};
struct TreeNode
{
int _val;
TreeNode* _left;
TreeNode* _right;
TreeNode()
:_val(0)
, _left(nullptr)
, _right(nullptr)
{}
};
void TestObjectPool()
{
// 申请释放的轮次
const size_t Rounds = 5;
// 每轮申请释放多少次
const size_t N = 100000;
std::vector<TreeNode*> v1;
v1.reserve(N);
size_t begin1 = clock();
for (size_t j = 0; j < Rounds; ++j)
{
for (int i = 0; i < N; ++i)
{
v1.push_back(new TreeNode);
}
for (int i = 0; i < N; ++i)
{
delete v1[i];
}
v1.clear();
}
size_t end1 = clock();
std::vector<TreeNode*> v2;
v2.reserve(N);
ObjectPool<TreeNode> TNPool;
size_t begin2 = clock();
for (size_t j = 0; j < Rounds; ++j)
{
for (int i = 0; i < N; ++i)
{
v2.push_back(TNPool.New());
}
for (int i = 0; i < N; ++i)
{
TNPool.Delete(v2[i]);
}
v2.clear();
}
size_t end2 = clock();
cout << "new cost time:" << end1 - begin1 << endl;
cout << "object pool cost time:" << end2 - begin2 << endl;
}
Debug版本
32位
64位
Release版本
32位
64位
清晰看出,ConcurrentMemoryPool的高效