前几天看完了侯捷老师的内存管理这门课,想动手实现一个简易版的内存池这里记录一下:
为什么需要内存池
首先我们需要了解malloc申请一块内存时,内存的布局情况如下图:我们会发现实际的内存比我们申请的内存要多出许多东西,首先时上下两个cooki,cooki记录的是申请内存的大小和内存的状态,具体可以看一下侯老师的视频。
如果我们一直用malloc申请内存,会浪费许多不必要的内存,那么怎样才能解决这种办法呢,那就是内存池.
什么是内存池
内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。
实现内存池
1、首先malloc想内存申请一大块内存,一般是20sizeof()+战备池,战备池的大小和累计申请内存有关系,但是,我们不可能为每一个sizeof()大小都申请内存,如果这样实现比较麻烦,并且不易于管理,所以实现的时候我们8,16…128分块申请,如果要申请sizeof()内存不够,向上元整,例如要申请的内存大小sizeof()为9,实际我们为他申请的是16的大小。以此类推。这里我们用一个数组实现数组中的每一项负责一个固定大小的内存。ROUNDUP()这个函数实现向上元整,如图:
运行实际:
1、例如我们要申请32bytes,由于pool为空所以我们需要malloc分配一大块内存:大小为3220*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]然后索取104202+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]中,然后索取7220*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