【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.有没有一种可能性,大内存块切了一部分,同时又有一部分内存还回来了,这时候接下来如果要继续申请内存,该怎么处理呢?是用原来的大内存块继续切还是用还回来的内存呢?

全部代码:

cpp 复制代码
#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模式:

相关推荐
kill bert3 小时前
Java八股文背诵 第四天JVM
java·开发语言·jvm
你是理想6 小时前
wait 和notify ,notifyAll,sleep
java·开发语言·jvm
returnShitBoy9 小时前
Go语言中的垃圾回收是如何工作的?
java·jvm·golang
liwulin050615 小时前
【JAVA】JVM 堆内存“缓冲空间”的压缩机制及调整方法
java·开发语言·jvm
八股文领域大手子15 小时前
从接口400ms到20ms,记录一次JVM、MySQL、Redis的混合双打
jvm·数据库·redis·mysql·jar
佩奇的技术笔记15 小时前
Java学习手册:JVM、JRE和JDK的关系
java·开发语言·jvm
qian_qh1 天前
如何判断JVM中类和其他类是不是同一个类
jvm
魔道不误砍柴功1 天前
Java性能调优2025:从JVM到Kubernetes的全链路优化策略
java·jvm·kubernetes
bing_1581 天前
JVM 如何分析 GC 日志,定位 GC 性能问题?
jvm·定位gc性能·分析gc日志
斗锋在干嘛2 天前
Android 回答视频边播放边下载的问题
android·jvm·音视频