目录
1.定长池基础框架
//定长内存池
template<class T>
class ObjectPool
{
public:
inline static void* SystemAlloc(size_t kpage)//向系统申请内存的函数
{
#ifdef _WIN32
void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
// linux下brk mmap等
#endif
if (ptr == nullptr)
throw std::bad_alloc();
return ptr;
}
T* New()
{}
void Delete(T* obj)
{}
private:
char* _memory = nullptr; //向系统申请的一大块内存,申请了多少字节就从起始地址++多少次,所以定义为char*
void* _freeList = nullptr; //还回来的空间的过程中,链接的自由链表的头指针
size_t _surplusBytes = 0; //定长池剩余的空间大小(字节为单位)
};
2.New的实现
- 当自由链表中有空间时,应该优先去自由链表中拿,而不是给它切分内存
- 当定长池剩余的空间不足时,需要重新申请空间
- 若对象的大小小于4/8个字节,那么它就无法存储下一个位置的地址,此时需要将它直接扩为4/8字节来使用
原理图
代码
T* New()
{
T* obj = nullptr;
//1.优先使用_freeList指向的空间
if (_freeList)//进行头删,利用前4/8个字节标记下一个结点
{
obj = (T*)_freeList;
_freeList = *(void**)_freeList;
}
else//2.使用_memory指向的空间
{
if (_remainBytes < sizeof(T))//_memory空间不足
{
_memory = (char*)malloc(128 * 1024);
_remainBytes = 128 * 1024;
if (_memory == nullptr)
{
throw std::bad_alloc();
}
}
//_memory空间充足
int szOfT = sizeof(T);
size_t step = szOfT < sizeof(void*) ?
sizeof(void*) : szOfT;
obj = (T*)_memory;
_memory += step;
_remainBytes -= szOfT;
}
//使用定位new 显示T的调用构造函数
new(obj) T;
return obj;
}
3.Delete的实现
void Delete(T* obj)//头插到_freeList
{
//显示调用析构函数
obj->~T();
*(void**)obj = _freeList;
_freeList = obj;
}
4.定位new的使用
C++中的定位new是一种高级内存分配技术,允许程序员在已经分配的内存上创建对象。
这在某些情况下非常有用,例如在需要在特定的内存地址上创建对象时,或者在需要管理内存分配的高性能应用程序中。
#include <iostream>
#include <new>
class MyClass {
public:
MyClass(int val) : value(val) {}
void printValue() { std::cout << "Value: " << value << std::endl; }
private:
int value;
};
int main() {
// 分配内存
char buffer[sizeof(MyClass)];
// 在已分配的内存上使用定位new创建对象
MyClass* obj = new(buffer) MyClass(42);
// 使用对象
obj->printValue();
// 手动调用析构函数
obj->~MyClass();
return 0;
}
5.对比new(malloc)性能测试
测试代码
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:
release:
6.系统调用VirtualAlloc代替malloc
我们的ObjctPool的工作原理与malloc基本一致.
7.ObjectPool源码
#ifdef _WIN32
#include<windows.h>
#else
#endif
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else //8kb的整数倍
// linux下brk mmap等
#endif
if (ptr == nullptr)
throw std::bad_alloc();
return ptr;
}
//T的大小为每次申请,释放空间的单位数
template<class T>
class ObjectPool
{
public:
T* New()
{
T* obj = nullptr;
//1.优先使用_freeList指向的空间
if (_freeList)//进行头删,利用前4/8个字节标记下一个结点
{
obj = (T*)_freeList;
_freeList = *(void**)_freeList;
}
else//2.使用_memory指向的空间
{
if (_remainBytes < sizeof(T))//_memory空间不足
{
//_memory = (char*)malloc(128 * 1024);
//_remainBytes = 128 * 1024;
_remainBytes = 128 * 1024;
_memory = SystemAlloc(_remainBytes >> 13);
}
//_memory空间充足
int szOfT = sizeof(T);
size_t step = szOfT < sizeof(void*) ?
sizeof(void*) : szOfT;
obj = (T*)_memory;
_memory += step;
_remainBytes -= szOfT;
}
//使用定位new 显示T的调用构造函数
new(obj) T;
return obj;
}
void Delete(T* obj)//头插到_freeList
{
//显示调用析构函数
obj->~T();
*(void**)obj = _freeList;
_freeList = obj;
}
private:
char* _memory = nullptr;//指向定长池申请的大块内存
void* _freeList = nullptr;//指向申请后又归还的自由链表
size_t _remainBytes = 0;//_memory中还有多少字节可以申请
};