一、定义
内存池是一种内存分配方式,也被称为固定大小区块规划。内存池是在真正使用内存之前,先申请分配一定数量的、大小相等的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。
优点
- 性能提升:由于内存池中的内存块是预先分配好的,因此可以快速响应内存分配请求,避免了动态内存分配时的系统调用开销。
- 减少内存碎片:当进程长期运行malloc和free进行内存的分配与销毁时,因为是随机进行的,会产生内存碎片。而内存池中的内存块是预先分配和设置好的,因此不会产生内存碎片。
- 内存泄漏预防:由于内存池的使用是成块管理,可以在程序退出时一次性释放全部内存,从而避免了内存泄漏的风险。
二、固定大小内存块的内存池
首先为内存池分配一大块内存块,并将其划分为多个固定大小的小块内存。每个小块内存的头部存储下一个小块内存的地址。
内存池结构体为:
typedef struct mempool_s{
char *mem;
char *free_ptr;
int blocksize;
int freecount;
}mempool_t;
mem指向内存池的起始位置。
free_ptr指向空内存块的起始位置。
blocksize为内存池的大小。
freecount为空内存块的数量。
创建内存池
int memp_create(mempool_t *m, int block_size){
//如果结构体不存在,返回错误值
if(!m)return -1;
//内存块的大小
m->blocksize = block_size;
//空闲内存块的数量
m->freecount = MEM_PAGE_SIZE / block_size;
//创建预先设定好内存池大小
m->mem = (char *)malloc(MEM_PAGE_SIZE);
//创建失败
if(!m->mem){
return -2;
}
memset(m->mem, 0, MEM_PAGE_SIZE);
//空闲内存块的地址
m->free_ptr = m->mem;
int i = 0;
char *ptr = m->mem;
//为每个内存块前面4或者8个字节设置为下一个内存块的地址(32位系统指针大小为4,64位系统指针大小为8)
for(i=0; i < m->freecount; i++){
*(char **)ptr = ptr + block_size;
ptr = ptr + block_size;
}
//最后一个内存块指向空
*(char **)ptr = NULL;
return 0;
}
销毁内存池
void memp_destory(mempool_t *m) {
if (!m) return ;
free(m->mem);
}
由于这块内存在物理上是连续的,因此只需要释放内存池的基地址这阵就可以释放整块内存,无需逐个释放内存块。
分配内存块
void memp_alloc(mempool_t *m){
//如果内存池不存在或者没有空闲的内存块
if(!m || m->freecount == 0) return NULL;
//取出第一个空闲的内存块
void *ptr = m->free_ptr;
//取出空闲内存块指向的下一个内存块,并将其设置为当前空闲内存块
m->free_ptr = *(char **)ptr;
//空闲内存块数量减一
m->freecount --;
//返回内存块
return ptr;
}
销毁内存块
void memp_free(mempool_t *m, void *ptr){
//将要释放的内存块头部指向空内存块
*(char **)ptr = m->free_ptr;
//将空内存块指针指向释放的内存块
m->free_ptr = (char *)ptr;
m->freecount ++;
}