看完了之前两套代码后,我打算自己动手实现一个简单的内存池,我的思路与代码二类似,这样做的好处是可以给不同的对象使用不同的内存池,可以相互隔绝:memorypool下面挂一个memory的链表,可以选择动态扩展长度,每次分配和回收都以memory为单位;memorypool中的free_list 采用单链表实现,used_list采用双链表实现,因为考虑到,分配给某个对象实例一个内存空间后,方便查看该对象还有哪些实例,所以used_list采用了双链表。
1.数据的组织结构:
{
int mempool_size;
int memory_size;//block_size + sizeof(manager information) = mem_size
int block_size;
int total_memory_cnt;
int used_memory_cnt;
int auto_extend;
struct Memory* free_list;
struct Memory* used_list;
}memorypool;
{
int memory_size;
int block_size;
struct Memory* prev;
struct Memory* next;
struct Memorypool* mp;
char* block;
}memory
memorypool是memory的信息管理;memory是向系统申请的内存块,包括信息管理部分和实际分配给用户使用的部分;
2.该内存池对外提供的API:
memorypool* memorypool_create(int block_request_size, int memory_init_quantity, bool memory_extend);
char* memory_malloc(memorypool* mp,int data_size);
int memorypool_info_get(memorypool* mp);
int memory_free(char** p_bk);
int memorypool_destroy(memorypool* mp);
3.源码:
头文件:memorypool.h
#ifndef _MEMORYPOOL_H_
#define _MEMORYPOOL_H_
#include
#include
#include
typedef struct Memorypool
{
int mempool_size;
int memory_size;//block_size + sizeof(manager information) = mem_size
int block_size;
int total_memory_cnt;
int used_memory_cnt;
int auto_extend;
struct Memory* free_list;
struct Memory* used_list;
}memorypool;
typedef struct Memory
{
int memory_size;
int block_size;
struct Memory* prev;
struct Memory* next;
struct Memorypool* mp;
char* block;
}memory;
memorypool* memorypool_create(int block_request_size, int memory_init_quantity, bool memory_extend);
char* memory_malloc(memorypool* mp,int data_size);
int memorypool_info_get(memorypool* mp);
int memory_free(char** p_bk);
int memorypool_destroy(memorypool* mp);
memory* memory_creat(int memory_size, memorypool* mp);
#endif
源文件:memorypool.c
#include "memorypool.h"
memorypool* memorypool_create(int block_request_size, int memory_init_quantity, bool memory_extend)
{
memorypool* mp = NULL;
mp = (memorypool*)malloc(sizeof(memorypool));
memset(mp, 0, sizeof(memorypool));
mp->block_size = block_request_size;
mp->memory_size = block_request_size + sizeof(memory);
mp->auto_extend = memory_extend;
for (int i = memory_init_quantity; i > 0; i--)
{
memory* mm = NULL;
mm = memory_creat(mp->memory_size , mp);
}
return mp;
};
char* memory_malloc(memorypool* mp, int data_size)
{
memory* p_memory = NULL;
if (mp->block_size < data_size)
{
return NULL;
}
if (NULL == mp->free_list)
{
if (mp->auto_extend == 1)
memory_creat(mp->memory_size, mp);
else
return NULL;
}
p_memory = mp->free_list;
mp->free_list = mp->free_list->next;
p_memory->next = NULL;
if (mp->used_list)
{
mp->used_list->prev->next = p_memory;
p_memory->prev = mp->used_list->prev;
}
else
{
mp->used_list = p_memory;
}
p_memory->next = mp->used_list;
mp->used_list->prev = p_memory;
mp->used_list = p_memory;
mp->used_memory_cnt += 1;
return p_memory->block;
};
int memorypool_info_get(memorypool* mp)
{
printf("current mempool_size:%d\n\
current memory_size:%d\n\
current block_size:%d\n\
total_memory_cnt:%d\n\
used_memory_cnt:%d\n",mp->mempool_size,mp->memory_size,mp->block_size, \
mp->total_memory_cnt,mp->used_memory_cnt);
return 0;
};
int memory_free(char** p_bk)
{
memory* p_memory = (memory* )(*p_bk + sizeof(memory));//错误
memory* p_memory = (memory* )(*p_bk - sizeof(memory)/(sizeof(char)));//正确
p_memory->next->prev = p_memory->prev;
p_memory->prev->next = p_memory->next;
p_memory->next = NULL;
p_memory->prev = NULL;
if (p_memory->mp->used_memory_cnt == 1)
p_memory->mp->used_list = NULL;
p_memory->next = p_memory->mp->free_list;
p_memory->mp->free_list = p_memory;
p_memory->mp->used_memory_cnt -= 1;
*p_bk = NULL;
return 0;
};
int memorypool_destroy(memorypool* mp)
{
memory* mm = NULL;
if (mp->free_list)
{
mm = mp->free_list;
mp->free_list = mp->free_list->next;
free(mm);
mm = NULL;
}
if (mp->used_list)
{
mp->used_list->prev->next = NULL;
mm = mp->used_list;
mp->used_list = mp->used_list->next;
free(mm);
mm = NULL;
}
free(mp);
return 0;
}
memory* memory_creat(int memory_size, memorypool* mp)
{
memory* mm = NULL;
char* bk = NULL;
char* memory_start = (char*)malloc(memory_size);
memset(memory_start, 0, memory_size);
mm = (memory*)memory_start;
memset(mm, 0, sizeof(memory));
bk = memory_start - sizeof(memory);//错误
bk =memory_start +sizeof(memory)/(sizeof(char));//正确
mm->memory_size = memory_size;
mm->block_size = memory_size - sizeof(memory);
mm->mp = mp;
mm->block = bk;
mm->next = mp->free_list;
mp->free_list = mm;
mp->total_memory_cnt += 1;
mp->mempool_size += mp->memory_size;
return mm;
};
测试文件:main.c
#include "memorypool.h"
#include "time.h"
int main()
{
memorypool* mp = NULL;
int memory_init_quantity = 5;
bool memory_extend = 1;
int block_request_size =1024;
time_t start = 0;
time_t end = 0;
int count = 0;
mp = memorypool_create(block_request_size, memory_init_quantity, memory_extend);
struct test
{
int a;
};
struct test* p_data;
start = time(NULL);
while (1)
{
p_data = (struct test*)memory_malloc(mp, sizeof(struct test));
//p_data = (struct test*)malloc(sizeof(struct test));
p_data->a = 100;
memory_free((char**)&p_data);
//free(p_data);
end = time(NULL);
count += 1;
if (end == (start + 300))
{
printf("%d", count);
break;
}
}
/*
p_data = (struct test*)memory_malloc(mp, sizeof(struct test));
memory_free((char**)&p_data[0]);
memorypool_info_get(mp);
memorypool_destroy(mp);
*/
system("pause");
return 0;
}
4.遇到的问题
一开始,我在写int memory_free(char* bk)函数时,纠结于p_data的类型是struct test*,与形参类型char*不一致,使用了笨办法,强制转换指针类型;
之后,考虑到memory_free()之后,被free掉的指针,仍然可以访问,我索性把要free的指针的地址传入了函数,在memory_free()函数内将该指针置为了NULL;
最后,在对比memory_malloc\free与系统函数malloc\free的性能时,突然想起看看系统函数是怎么处理这个问题的,发现事情其实是这样的:系统函数malloc返回的是void*(无类型,即所有类型),我们在使用该地址时需要强制转换为我们需要的指针类型;而free函数的形参是void*,我们传进去的实参,无论是何种类型的指针,均可以隐式的转换为void*,使用了系统函数free()之后,free的指针也依然存在且可以访问,为了避免出现野指针,我们需要手动将其置为NULL;但指针的指针没有这样的用法。
5.收获
对指针的类型转换有了进一步的理解:void*需要强制转换为其他类型;而其他类型的指针可以隐式转换为void*;指针的指针只能强制转换,没有这种隐式转换。
相同时间内反复malloc和free,内存池的效率大概是系统函数的3倍;
2019/2/17:
在两个线程分别向内存池申请内存块、释放内存块到内存池时,出现了mp指针的指向错误,
经解决,是内存池在memory_creat和memory_free时,对p_memory->block指针赋值错误造成的。
该bug将在上面代码中标记出来。
犯这样的错误是因为没有搞清楚:1.地址+size,想得到地址,因注意size的进制、量度;
2.堆是从低地址开始分配的,栈是从高地址开始分配的。没有区分地址分配的方向。见下图。