原理
还回来的内存用链表串联起来,称为自由链表
内存块自身进行链接,前四个字节存下一个的地址
结构
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); //分配初始的大内存128byteif (_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); //分配初始的大内存128byteif (_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); //分配初始的大内存128byteif (_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); //分配初始的大内存128byteif (_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的高效