c++实现简易内存池

参考:

https://blog.csdn.net/K346K346/article/details/49538975

https://blog.csdn.net/xjtuse2014/article/details/52302083#comments

提高c++性能的编程技术 https://blog.csdn.net/fengbingchun/article/details/84497625

https://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html

内存池简介

内存池是池化技术中的一种形式。通常我们在编写程序的时候回使用 new delete 这些关键字来向操作系统申请内存,而这样造成的后果就是每次申请内存和释放内存的时候,都需要和操作系统的系统调用打交道,从堆中分配所需的内存。如果这样的操作太过频繁,就会找成大量的内存碎片进而降低内存的分配性能,甚至出现内存分配失败的情况。

而内存池就是为了解决这个问题而产生的一种技术。从内存分配的概念上看,内存申请无非就是向内存分配方索要一个指针,当向操作系统申请内存时,操作系统需要进行复杂的内存管理调度之后,才能正确的分配出一个相应的指针。而这个分配的过程中,我们还面临着分配失败的风险。(和os打交道很麻烦,会有额外开销,直接在内存池中申请内存)

所以,每一次进行内存分配,就会消耗一次分配内存的时间,设这个时间为 T,那么进行 n 次分配总共消耗的时间就是 nT;如果我们一开始就确定好我们可能需要多少内存,那么在最初的时候就分配好这样的一块内存区域,当我们需要内存的时候,直接从这块已经分配好的内存中使用即可,那么总共需要的分配时间仅仅只有 T。当 n 越大时,节约的时间就越多。

从大小的角度分为以下两种:

(1)、固定大小:分配固定大小内存块的内存管理器。

(2)、可变大小:分配任意大小内存块的内存管理器。所请求分配的大小事先是未知的。

类似的,从并发的角度也分为以下两种:

(1)、单线程:内存管理器局限在一个线程内。内存被一个线程使用,并且不越出该线程的界限。这种内存管理器不涉及相互访问的多线程。

(2)、多线程:内存管理器被多个线程并发地使用。这种实现需要包含互斥执行的代码段。无论什么时候,只能有一个线程在执行一个代码段。


经典内存池的设计
(1)先申请一块连续的内存空间,该段内存空间能够容纳一定数量的对象;
(2)每个对象连同一个指向下一个对象的指针一起构成一个内存节点(Memory Node)。各个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间;
(3)某个内存节点一旦分配出去,从空闲内存节点链表中去除;
(4)一旦释放了某个内存节点的空间,又将该节点重新加入空闲内存节点链表;
(5)如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。
 


// mempool.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <cassert>
#include <ctime>

using namespace std;

const int max = 10000000;
template<int size_obj,int Num_obj=10>//前面是对象的字节数,后面是每一个块中节点(对象)数量
class  MemoryPool
{
public:
	MemoryPool()noexcept;
	~MemoryPool() noexcept;
	void* alloc();
	void expand_freelist();
	void free(void*p);

private:
	struct FreeNode//空闲节点 链表 
	{
		FreeNode*next;
		char val[size_obj];//节点数据域大小和对象size一样
	};
	struct MemBlock//块结构体 链表 
	{
		FreeNode val[Num_obj];
		MemBlock *next;
	};
	FreeNode *freenodehead;//当前第一个能用的空闲节点
	MemBlock *memblockhead;//当前第一个能用的内存块
};
template<int size_obj, int Num_obj >
MemoryPool<size_obj,  Num_obj>::MemoryPool() noexcept
{
	freenodehead = nullptr;
	memblockhead = nullptr;
}
 template<int size_obj, int Num_obj >
 MemoryPool<size_obj, Num_obj>::~MemoryPool() noexcept
{
	 MemBlock *ptr;
	 while (memblockhead)
	 {
		 ptr = memblockhead->next;
		 delete memblockhead;
		 memblockhead = ptr;
	 }
}
 template<int size_obj, int Num_obj >
 void *MemoryPool<size_obj, Num_obj>::alloc() 
 {
	 if (freenodehead==nullptr) //如果没有空闲队列了
	 {
		 expand_freelist();
	 }
	 void *ptr = freenodehead;
	 freenodehead = freenodehead->next;
	 return ptr;
 }
 template<int size_obj, int Num_obj >
 void MemoryPool<size_obj, Num_obj>::expand_freelist() //没有空闲链表了 新创建块和链表 新创建的块插到头部
 {
	 //块之间也是链表 
	 //不用担心快之间链表没有联系 执行释放的话 会把他们连在一起的
	 MemBlock *newblock = new MemBlock();
	 newblock->next = nullptr;
	 freenodehead = &newblock->val[0];//新建的块的元素赋值给空闲节点
	 //把新建的节点连起来(释放之后 连的顺序就不一定了)
	 for (int i = 0; i < Num_obj-1; i++) 
	 {
		 newblock->val[i].next = &newblock->val[i+1];
	 }
	 newblock->val[Num_obj - 1].next = nullptr;
	 if (memblockhead == nullptr) //没有第一个块 他就是第一个
	 {
		 memblockhead = newblock;
	 }
	 else//已经有了 把他插入到第一个
	 {
		 newblock->next = memblockhead;
		 memblockhead = newblock;
	 }

 };
 template<int size_obj, int Num_obj >
void MemoryPool<size_obj, Num_obj>::free(void*p)
 {
	FreeNode *ptr = (FreeNode*)p;
	ptr->next = freenodehead;
	freenodehead = ptr;
 };
class Test//测试类
{
public:
	Test() {};
	explicit Test(int x):val(x) {};
	~Test() {};

private:
	int val;
};

int main()
{
	
	clock_t start;
	MemoryPool<sizeof(int)> mempool;
	
	start = clock();
	for (int i = 0; i < max; i++) 
	{
		//Test *t1 = (Test*)mempool.alloc();
		int *t1 = (int*)mempool.alloc();
		//p1->print();
		mempool.free(t1);
	}
	
	std::cout << "new Allocator Time: ";
	std::cout << (((double)clock() - start) / CLOCKS_PER_SEC) << "\n\n";
	
	start = clock();
	for (int i = 0; i < max; i++)
	{
		//Test *t2 = new Test();
		int *t2 = new int();
		delete t2;
	}
	
	std::cout << "old Allocator Time: ";
	std::cout << (((double)clock() - start) / CLOCKS_PER_SEC) << "\n\n";
	
	return 0;


}



// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门提示: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
new Allocator Time: 0.375

old Allocator Time: 2.599

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值