简单内存池与定长内存池

Posted 楠c

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单内存池与定长内存池相关的知识,希望对你有一定的参考价值。

内存碎片

在这里插入图片描述

最简单的内存池

申请

在这里插入图片描述

释放

在这里插入图片描述

定长内存池

在这里插入图片描述

哈希映射的freelist池

在这里插入图片描述
内碎片
在这里插入图片描述
对比内碎片与外碎片
在这里插入图片描述

实现定长内存池

成员变量

	private:
		//一大块内存
		//char*,+1就是一个字节,容易移动。
		char* _memory = nullptr;
		//自由链表
		//不要节点,存储指针
		void* _freeList = nullptr;
		//剩余容量
		size_t leftSize=0;

使用这个自由链表,将从memory上的对象挂在自由链表
上,同时memory向后移动
在这里插入图片描述
挂在freelist上
在这里插入图片描述
怎么实现挂起来呢?在下面代码中取内存的头四个字节挂起来的。但是64位下不好办。
在这里插入图片描述
怎么解决呢?
在这里插入图片描述

但假如你的类型就比较小呢?没办法,只能定义一个静态变量。
在这里插入图片描述

namespace zjn{
	template<class T>
	class ObjectPool
	{
	public:
		~ObjectPool()
		{}
		//下一个对象,返回值可以是引用
		void*& NextObj(void* obj)
		{
			return *((void**)obj);
		}
		T*  New()
		{
			//有freeList就去找freelist
			T* obj = nullptr;
			if (_freeList)
			{
				
				obj = (T*)_freeList;
				//他头四个字节就是下一个的地址,让下一个做新的头
				/*_freeList = (void*)*((int*)_freeList);*/
				_freeList = NextObj(obj);
			}
			else//否则就去看大块内存
			{
				//大块内存也没有,去malloc
				if (leftSize<sizeof(T))
				{
					//先申请100k
					leftSize = 1024 * 100;
					_memory = (char*)malloc(leftSize);
					//失败抛异常
					if (_memory == nullptr)
					{
						throw std::bad_alloc();
					}
				}
				//走到这两种情况
				//1._memory存在,从他上面切割
				//2._memory不存在,malloc开出来了
				//开空间+初始化
				obj = (T*)_memory;
				_memory += sizeof(T);
				leftSize -= sizeof(T);

			}
			new(obj)T;//定位new
			return obj;
		}
		//伪删除,实际是放到freelist中
		void Delete(T* obj)
		{
			obj->~T();
			//取出前四个字节让他指向上一个头
			/**((int*)obj) = (int)_freeList;*/
			NextObj(obj) = _freeList;

			//让freeList指向新的头
			_freeList = obj;
		}
	};

最开始是这样写的,但是有越界可能
在这里插入图片描述
在这里插入图片描述

自定义一个类型,用一个节点测试

测试代码

struct TreeNode
	{
		int val;
		TreeNode* left;
		TreeNode* right;
		TreeNode()
			:val(0)
			, left(nullptr)
			, right(nullptr)
		{}
	};
	void TestObjectPool()
	{
		ObjectPool<TreeNode> tnPool;

		for (size_t i = 0; i < 1000; ++i)
		{
			TreeNode* node = tnPool.New();
			cout << node << endl;
		}
		
	}

在这里插入图片描述
这个测试的是,之前释放掉的内存,在申请和之前的地址一样。证明了我们复用是成功的。

	void TestObjectPool1()
	{
		ObjectPool<TreeNode> tnPool;
		TreeNode* node1 = tnPool.New();
		TreeNode* node2 = tnPool.New();
		TreeNode* node3 = tnPool.New();
		cout << "node1:" << node1 << endl;
		cout << "node2:" << node2 << endl;
		cout << "node3:" << node3 << endl;
		tnPool.Delete(node2);
		TreeNode* node4 = tnPool.New();
		cout << "node4:" << node4 << endl;
	}

在这里插入图片描述

	void TestTime()
	{
		size_t begin1=clock();
		std::vector<TreeNode*> v1;
		for (int i = 0; i < 100000; ++i)
		{
			v1.push_back(new TreeNode);
		}
		for (int i = 0; i < 100000; ++i)
		{
			delete v1[i];
		}
		v1.clear();
		for (int i = 0; i < 100000; ++i)
		{
			v1.push_back(new TreeNode);
		}
		size_t end1 =clock();
		cout << "new and delete:   "<<end1 - begin1 << endl;

		ObjectPool<TreeNode> tnPool;
		size_t begin2 = clock();
		std::vector<TreeNode*> v2;
		for (int i = 0; i < 100000; ++i)
		{
			v2.push_back(tnPool.New());
		}
		for (int i = 0; i < 100000; ++i)
		{
			tnPool.Delete(v2[i]);
		}
		v2.clear();
		for (int i = 0; i < 100000; ++i)
		{
			v2.push_back(new TreeNode);
		}
		size_t end2 = clock();
		cout <<"New() and Delete():   "<<end2 - begin2 << endl;
	}
};

之前的释放只是挂在freelist中,那真正的释放是怎么实现呢?
在这里插入图片描述

以上是关于简单内存池与定长内存池的主要内容,如果未能解决你的问题,请参考以下文章

简单实现定长内存池

内存池与内存块

开胃菜-定长内存池

开胃菜-定长内存池

开胃菜-定长内存池

GIL 线程池与进程池 同步与异步