c++分配器简易版内存池的实现

前几天看完了侯捷老师的内存管理这门课,想动手实现一个简易版的内存池这里记录一下:

为什么需要内存池

首先我们需要了解malloc申请一块内存时,内存的布局情况如下图:我们会发现实际的内存比我们申请的内存要多出许多东西,首先时上下两个cooki,cooki记录的是申请内存的大小和内存的状态,具体可以看一下侯老师的视频。
如果我们一直用malloc申请内存,会浪费许多不必要的内存,那么怎样才能解决这种办法呢,那就是内存池.
在这里插入图片描述

什么是内存池

内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。

实现内存池

1、首先malloc想内存申请一大块内存,一般是20sizeof()+战备池,战备池的大小和累计申请内存有关系,但是,我们不可能为每一个sizeof()大小都申请内存,如果这样实现比较麻烦,并且不易于管理,所以实现的时候我们8,16…128分块申请,如果要申请sizeof()内存不够,向上元整,例如要申请的内存大小sizeof()为9,实际我们为他申请的是16的大小。以此类推。这里我们用一个数组实现数组中的每一项负责一个固定大小的内存。ROUNDUP()这个函数实现向上元整,如图:
在这里插入图片描述运行实际:
1、例如我们要申请32bytes,由于pool为空所以我们需要malloc分配一大块内存:大小为32
20*2+ROUNDUP(0>>4)=1280;注意此时只有一个cooki

在这里插入图片描述2、紧接着申请64bytes的带下,由于上边战备池中还留有640大小,所以要利用战备池中的容量,640/64=10个,一个客户,其余9个挂在负责64bytes大小的链表下:
在这里插入图片描述

3、之后申请96大小的内存,pool中也就是上图中深蓝色没有余量,需要malloc申请一大块内存96202+ROUNDUP(1280>>4)=3920;第一个给客户,其余19个挂在负责96的链表下

在这里插入图片描述4、申请88,由于pool中有2000,2000/88=22,取出20个,第一个给客户,其余19个挂在负责88的链表下面,pool剩余量2000-88*20=240;
在这里插入图片描述5、连续三次申请88,直接由list[10]给出三个;

在这里插入图片描述6、申请8bytes,由于list[0]没有,需要在pool中分割,pool中分割20个,第一个给客户,其余挂在list[0]下;pool剩余240-820=80;
在这里插入图片描述7、申请104bytes,list[12]没有区块,pool余量不足,又不能提供一个,先把剩余的80区块挂在list[9]然后索取104
202+ROUNDUP(5200>>4),第一个给客户,其余19个给list[12]
在这里插入图片描述
8、申请112,pool中有余量,现在pool中分割,第一个给客户,其余挂在list[13];pool = 168;
在这里插入图片描述
9、申请48,pool中有余量,pool中能分割3个,第一个给客户,其余挂在list[5],pool=24;
在这里插入图片描述10、申请72,list[8]没有可用区块,pool不足一个,现将剩余量挂在list[2]中,然后索取72
20*2+ROUNDUP(9688>>4),如果系统的整个大小为10000,现在无法申请更多,于是开始向右寻找list[9]回填pool,此时pool = 80-72=8;
在这里插入图片描述来看一下代码:

namespace zy{
	
enum {
	_ALIGN=8//最小区块
};

enum {
	_MAX_BYTES=128//最大区块
};
enum {
	_NFREELISTS=16//list有几个元素
};

class alloc{
	private:
		static size_t ROUND_UP(size_t bytes){//向上元整,例如申请7,实际将申请8;
			return(((bytes)+_ALIGN-1) & ~(_ALIGN-1) );
		}
		union obj{
		//嵌入式指针,这里节省空间在分块的时候使用指针,给用户的时候将指针抹除。
			union obj * free_list_link;//嵌入式指针 
		};
		static obj * free_list[_NFREELISTS];//16个链表
		static size_t FREELIST_INDEX(size_t bytes){//查找在list第几个元素
			return (((bytes)+_ALIGN-1)/_ALIGN-1);
		}
		
		static void *refill(size_t n);//这个函数负责交给用户申请的内存,并把内存分块用链表连接起来
		static char* chunk_alloc(size_t size, int &nobjs);//像pool中要内存,或者向malloc要内存
		static char* start_free;//pool的起点
		static char* end_free;//pool的终点
		static size_t heap_size;//累计申请大小
	public:
			static void * allocate(size_t n){
				std::cout <<"开始分配"<< n << "个内存"<<std::endl; 
				obj ** my_free_list;
				obj * result;
				if(n>(size_t)_MAX_BYTES){
					std::cout <<"分配完成"<< n << "个内存"<<std::endl; 
					return malloc(n);	
				}
				my_free_list =  free_list + FREELIST_INDEX(n);
				result = *my_free_list;
				if(result == 0){
					void *r = refill(ROUND_UP(n));
					std::cout <<"分配完成"<< n << "个内存"<<std::endl; 
					return r;
				}
				*my_free_list = result->free_list_link;
				std::cout <<"分配完成"<< n << "个内存"<<std::endl; 
				return result;
			}
			static void deallocate(void *p,size_t n){
				std::cout <<"开始释放"<< n << "个内存"<<std::endl; 
				if(n>(size_t) _MAX_BYTES){
					free(p);
					std::cout <<"释放完成"<< n << "个内存"<<std::endl; 
					return;
				}
				obj *q = (obj*)p;
				obj ** my_free_list;
				my_free_list = free_list + FREELIST_INDEX(n);
				q->free_list_link = *my_free_list;
				*my_free_list = q;
			}
			
};

先来看一下chunk_alloc函数

char* alloc::chunk_alloc(size_t size, int &nobjs){
				char* result;//返回指针
				size_t total_bytes = size * nobjs;//计算一共需要多少内存
				size_t bytes_left = end_free - start_free;//计算pool中还剩多少内存
				if(bytes_left >= total_bytes){//如果剩余量>申请量 那么直接返回客户,不需要多余的操作例上边第六个操作
					result = start_free;
					start_free = start_free+total_bytes;
					return (result);
				}else if (bytes_left >=size){//1<剩余<20例如第9步
					nobjs = bytes_left/size;
					total_bytes = nobjs * size;
					result = start_free;
					start_free += total_bytes;
					return result;
				}else{//不够的话向malloc要内存,pool中剩余的挂在相应的区块上
					size_t bytes_to_get = 2* total_bytes + ROUND_UP(heap_size >> 4);
					if(bytes_left >0){
						obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
						((obj*)start_free)->free_list_link = *my_free_list;
						*my_free_list = (obj*)start_free;
					}
					start_free = (char*)malloc(bytes_to_get);
					if(0==start_free){//malloc没有申请到内存,向list右边要内存,回填pool,分配
						int i;
						obj **my_free_list;
						obj *p;
						for(i = size;i<_MAX_BYTES;i +=_ALIGN){//找右边的内存看是否有用 
							my_free_list = free_list + FREELIST_INDEX(i);
							p = *my_free_list;
							if(0 != p){
								*my_free_list = p->free_list_link;
								start_free = (char *) p;//回挂pool
								end_free = start_free + i;
								return chunk_alloc(size,nobjs);
							}
						} 
						
					   
					}
					
					heap_size +=bytes_to_get;
					end_free = start_free + bytes_to_get;
					return(chunk_alloc(size,nobjs)) ;
				}
					
			}

refill函数

void * alloc::refill(size_t n){
				int nobjs = 20;
				char * chunk = chunk_alloc(n,nobjs);
				obj ** my_free_list;
				obj * result;
				obj * current_obj;
				obj * next_obj;
				int i;
				if(1==nobjs){
					return (chunk);
				}
				my_free_list = free_list + FREELIST_INDEX(n);
				result = (obj*)chunk;
				*my_free_list = next_obj = (obj*)(chunk +n);//这个是一个快的大小
				for(i=1;;i++){
					current_obj = next_obj;//申请到的内存,分块用链表链接
					next_obj = (obj*)((char*)next_obj+n);
					if(nobjs-1==i){
						current_obj->free_list_link = 0;
						break;
					}else{
						current_obj->free_list_link = next_obj;
						
					}
				}
				return (result);
				
			}

整体代码:

#include<new>
#include <cstddef>
#include <cstdio>
#include<stdlib.h> 
#include <iostream>


namespace zy{
	
enum {
	_ALIGN=8
};

enum {
	_MAX_BYTES=128
};
enum {
	_NFREELISTS=16
};

class alloc{
	private:
		static size_t ROUND_UP(size_t bytes){
			return(((bytes)+_ALIGN-1) & ~(_ALIGN-1) );
		}
		union obj{
			union obj * free_list_link;//嵌入式指针 
		};
		static obj * free_list[_NFREELISTS];
		static size_t FREELIST_INDEX(size_t bytes){
			return (((bytes)+_ALIGN-1)/_ALIGN-1);
		}
		
		static void *refill(size_t n);
		static char* chunk_alloc(size_t size, int &nobjs);
		static char* start_free;
		static char* end_free;
		static size_t heap_size;
	public:
			static void * allocate(size_t n){
				std::cout <<"开始分配"<< n << "个内存"<<std::endl; 
				obj ** my_free_list;
				obj * result;
				if(n>(size_t)_MAX_BYTES){
					std::cout <<"分配完成"<< n << "个内存"<<std::endl; 
					return malloc(n);	
				}
				my_free_list =  free_list + FREELIST_INDEX(n);
				result = *my_free_list;
				if(result == 0){
					void *r = refill(ROUND_UP(n));
					std::cout <<"分配完成"<< n << "个内存"<<std::endl; 
					return r;
				}
				*my_free_list = result->free_list_link;
				std::cout <<"分配完成"<< n << "个内存"<<std::endl; 
				return result;
			}
			static void deallocate(void *p,size_t n){
				std::cout <<"开始释放"<< n << "个内存"<<std::endl; 
				if(n>(size_t) _MAX_BYTES){
					free(p);
					std::cout <<"释放完成"<< n << "个内存"<<std::endl; 
					return;
				}
				obj *q = (obj*)p;
				obj ** my_free_list;
				my_free_list = free_list + FREELIST_INDEX(n);
				q->free_list_link = *my_free_list;
				*my_free_list = q;
			}
			
};

void * alloc::refill(size_t n){
				int nobjs = 20;
				char * chunk = chunk_alloc(n,nobjs);
				obj ** my_free_list;
				obj * result;
				obj * current_obj;
				obj * next_obj;
				int i;
				if(1==nobjs){
					return (chunk);
				}
				my_free_list = free_list + FREELIST_INDEX(n);
				result = (obj*)chunk;
				*my_free_list = next_obj = (obj*)(chunk +n);
				for(i=1;;i++){
					current_obj = next_obj;
					next_obj = (obj*)((char*)next_obj+n);
					if(nobjs-1==i){
						current_obj->free_list_link = 0;
						break;
					}else{
						current_obj->free_list_link = next_obj;
						
					}
				}
				return (result);
				
			}
			
char* alloc::chunk_alloc(size_t size, int &nobjs){
				
				char* result;
				size_t total_bytes = size * nobjs;
				size_t bytes_left = end_free - start_free;
				if(bytes_left >= total_bytes){
					result = start_free;
					start_free = start_free+total_bytes;
					return (result);
				}else if (bytes_left >=size){
					nobjs = bytes_left/size;
					total_bytes = nobjs * size;
					result = start_free;
					start_free += total_bytes;
					return result;
				}else{
					size_t bytes_to_get = 2* total_bytes + ROUND_UP(heap_size >> 4);
					if(bytes_left >0){
						obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
						((obj*)start_free)->free_list_link = *my_free_list;
						*my_free_list = (obj*)start_free;
					}
					start_free = (char*)malloc(bytes_to_get);
					if(0==start_free){//失败 
						int i;
						obj **my_free_list;
						obj *p;
						for(i = size;i<_MAX_BYTES;i +=_ALIGN){//找右边的内存看是否有用 
							my_free_list = free_list + FREELIST_INDEX(i);
							p = *my_free_list;
							if(0 != p){
								*my_free_list = p->free_list_link;
								start_free = (char *) p;
								end_free = start_free + i;
								return chunk_alloc(size,nobjs);
							}
						} 
						
					   
					}
					
					heap_size +=bytes_to_get;
					end_free = start_free + bytes_to_get;
					return(chunk_alloc(size,nobjs)) ;
				}
					
			}
char*  alloc::start_free = nullptr;
char*  alloc::end_free = nullptr;
size_t alloc::heap_size = 0;	
typename alloc::obj* alloc::free_list[_NFREELISTS]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
	
} 





测试代码

#include <iostream>
#include <complex>
#include <memory>				 //std::allocator  
#include "zy.h" //欲使用 std::allocator 以外的 allocator, 就得自行 #include <ext/...> 
using namespace std;
int main(){
	cout <<"\n\n\n测试分配器"<<endl;
	void *p1 = zy::alloc::allocate(120);
	void *p2 = zy::alloc::allocate(72);
	void *p3 = zy::alloc::allocate(60);
	cout <<p1<<' '<<p2<<' ' <<p3<<endl;
	zy::alloc::deallocate(p1,120);
	zy::alloc::deallocate(p2,72);
	zy::alloc::deallocate(p3,60);

}


有问题需要讨论的小伙伴,一起交流呀

运行结果:
在这里插入图片描述

源代码地址:
https://download.csdn.net/download/weixin_42326997/19929684

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值