【Buffer Pool】定长内存池的实现
创建一个大块的内存内存
1.内存的类型是什么? char* 方便有多少字节就乘以多少字节
2.如何还回来内存?可以将换回来的小块的内存块链接起来,使用freeList
3.如何链接起来? 让上一个内存块的数据存下一个内存块的地址即可
4.如果内存块的定长大小为20字节,该如何存储地址呢? 如果是32位下,指针大小为4字节,就划分内存的前4字节的空间存储地址。如果是64位下,就划分前8字节的空间存储地址。
5.如果内存块的定长大小不足4字节获得8字节呢?
6.如果没有剩余空间了,如何判断? 使用成员变量remainBytes,表示大块内存剩余的空间
7.第一次还回来了一个对象(内存),该怎么处理?让内存块头4字节或者8字节(一个指针的大小)指向空
7.1如何判断环境是32位还是64位? 对类型指针就行了,void是类型,void*是void的指针
*(void**)等于void*(对void*指针再解引用,为什么要这么写?因为这样写可以对该对象进行写入了),void*是指针类型,大小就是指针的大小
8.刚刚解决了第一次还回来的问题,那第二次第三次呢? 可以头插
并且,第一次和第二次是相同的,头插都可以解决
9.有没有一种可能性,大内存块切了一部分,同时又有一部分内存还回来了,这时候接下来如果要继续申请内存,该怎么处理呢?是用原来的大内存块继续切还是用还回来的内存呢?
全部代码:
#pragma once
#include<iostream>
#include<vector>
#include<time.h>
using std::cout;
using std::endl;template<class T>
class ObjectPool
{
public://申请内存T* New(){T* obj = nullptr;//1.优先把换回来的内存块再次重复利用if (_freeList) //代表有还回来的内存块对象{//重复利用,进行单链表的头删 //这里的*(void**)_freeList表示取freeLiset的前指针大小个字节 -->也就是取next的地址void* next = *((void**)_freeList); //? 这里为什么使用void**//obj是取整个对象obj = (T*)_freeList;//往后移动一位_freeList = next;}else //如果没有还回来的内存块{//判断开辟的一大块空间是否够这次申请的if (_remainBytes < sizeof(T)) {//如果不够_remainBytes = 1024 * 128; //申请128KB//这里为什么要这么写?_memory = (char*)malloc(_remainBytes);//SystemAlloc是系统调用接口//_memory = (char*)SystemAlloc(_remainBytes >> 13);//异常处理if (_memory == nullptr){throw std::bad_alloc();}}//创建一个对象指向memory,这个obj也就是我们申请内存给到的实体obj = (T*)_memory;//这里判断的意义是:如果我们申请的空间大于指针的大小,就返回我们申请的空间,//否则返回指针的大小 --> 这样做是为了内存块至少能存下地址size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T);//大块内存的指针向后移动_memory += objSize;//可用空间减少_remainBytes -= objSize;}new(obj) T;return obj;}//这是释放的过程,将释放的小内存块挂到_freeList前面void Delete(T* obj){//显式调用函数清理对象obj->~T();//头插//将obj内存块的前指针大小的字节存入_freeList的地址*(void**)obj = _freeList;//将_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模式
release模式: