前言
具体什么是内存池,不说了。我也是闲来无聊,自己实现一个内存池,也比较简单。
设计思路
我们采用C语言实现,所以,需要实现一些简单的容器。比如单链表。
总体大概设计思路示意图如上所示。
一个数组(或者向量(我们把原始的C语言数组,称之为数组,把类似的在堆空间中申请来的连续线线性空间称之为向量)),每一个节点是一个链表,每个链表里面挂着元素,元素的data指针便是我们内存池里面放的内存指针。
详细思路
第一:纲领数组我们按照索引,最小比如64,128,256 这样预先定义好。到了足够大之后,比如4k,我们便采用比如1k的颗粒度上涨。
第二:当申请的内存不足档位量的时候,我们便直接采用档位量。
第三:链表的策略
- 每个节点内部,我们维护两个链表。
0号链表,我们作为idle 空闲链表,申请的内存被释放之后,都存到这个数组里面。
1号链表,我们作为busy链表,我们存到这个里面。
这种做法,就是每次申请,都是0(1)复杂度,但是释放较为复杂,需要遍历busy链表,从中remove,然后插入回idle(push head 插入)。
所以申请 O(1);释放 O(n),n为当前该节点下的busy的个数。 - 每个纲领节点,一个链表,其中分为idle以及busy部分。前段idle,后段busy。为了复杂度尽量的低,所以,每次申请释放,我们尽量保持从头拿,然后插入尾。至于释放,需要遍历,单链表,那就只能从头部遍历,那么复杂度相对较高,(前面一堆是空的idle)。要么采用双链表,从尾部busy遍历。复杂度跟1一致,但是,内存使用多了一个prev指针。
- 还有一种,复杂度比较低,内存占用也会少一些,就是,每次申请出去的,我们不记录,就从idle里面拿出去,释放的时候,再插入进来。这种实现最简单,并且,复杂度最低。
第四:内存的申请方式。是整体采用一大块内存,然后分摊的方式,分到各个链表的data中。要么离散式,随用随申请,到一定数量之后,要么OOM报错,要么,一直这样,达到一定数量之后,会被有申请的被释放回来,就可以达到复用了。
实现
目录结构
项目支持windows以及linux系统。
不写make file了,回头单独开一遍博客,说明gyp的用法。
源码
FC.h
#ifndef __FC__H__
#define __FC__H__
#if defined _WIN32 || defined _WIN64
# define FC_WIN
# define FCAPI __stdcall
#elif defined __linux__
# define FC_LINUX
# define FCAPI
#endif
#endif // !__FC__H__
list.h && list.c
#ifndef __FC_LIST_LIST_H__
#define __FC_LIST_LIST_H__
#include "FC.h"
#include <stdio.h>
#include <stdlib.h>
#include "../FCMemPool.h"
#ifdef FC_WIN
# pragma pack(1)
struct FC_Node_T
{
// 记录 lvl,
struct FC_Node_T * next;
int lvl;
// 标记着该档位的大小,方便快速定位到对应的数组索引
char data[0];
};
# pragma pack()
#else
struct __attribute__((__packed__)) FC_Node_T
{
// 记录 lvl,
struct FC_Node_T * next;
int lvl;
// 标记着该档位的大小,方便快速定位到对应的数组索引
char data[0];
};
#endif // !FC_WIN
typedef struct FC_Node_T FC_Node;
typedef struct FC_List_T
{
FC_Node* head;
int available; // 当前可用的数量
int total_size; // 总体申请的数量
} FC_List;
#define FC_ListNode(ptr) (FC_Node*)((ptr) - sizeof(FC_Node))
#define FC_ListAvailable(list) (((FC_List*)(list))->available)
#define FC_ListSize(list) ((FC_List*)(list)->total_size)
#define FC_ListIncAvailable(list) (((FC_List*)(list))->available ++)
#define FC_ListDecAvailable(list) (((FC_List*)(list))->available --)
#define FC_ListIncSize(list) (((FC_List*)(list))->total_size ++)
#define FC_ListDecSize(list) (((FC_List*)(list))->total_size --)
FC_Node* FC_NodeCreate(FC_size_t size, int lvl);
FC_List* FC_ListCreate();
void FC_ListDestroy(FC_List* list);
void FC_ListAddHead(FC_List* list, FC_Node* node);
FC_Node* FC_ListPopHead(FC_List* list);
#endif // !__FC_LIST_LIST_H__
list.c
#include "list.h"
FC_Node * FC_NodeCreate(FC_size_t size, int lvl)
{
void* data = NULL;
FC_Node* node = (FC_Node*)malloc(sizeof(FC_Node) + size);
if (node == NULL)
{
return NULL;
}
node->lvl = lvl;
node->next = NULL;
return node;
}
FC_List * FC_ListCreate()
{
FC_List * list = (FC_List*)malloc(sizeof(FC_List));
if (list == NULL)
{
return NULL;
}
list->available = 0;
list->total_size = 0;
list->head = NULL;
return list;
}
void FC_ListDestroy(FC_List * list)
{
if (list == NULL)
{
return;
}
FC_Node* tmp = NULL;
while (list->head)
{
tmp = list->head;
list->head = list->head->next;
free(tmp);
}
free(list);
return ;
}
void FC_ListAddHead(FC_List * list, FC_Node * node)
{
if (list == NULL || node == NULL)
{
return;
}
node->next = list->head;
list->head = node;
}
FC_Node * FC_ListPopHead(FC_List * list)
{
if (list == NULL)
{
return NULL;
}
FC_Node* node = list->head;
if (list->head)
{
list->head = list->head->next;
}
if (node)
{
node->next = NULL;
}
return node;
}
FCMemPool.h
#ifndef __FC_MEM_MGR_H__
#define __FC_MEM_MGR_H__
#include "FC.h"
#include <stdio.h>
#include <stdlib.h>
#define MAX_COUNT_THREHOLD (32)
typedef size_t FC_size_t;
typedef struct FC_List_T FC_List;
typedef struct FCMemPool_T
{
FC_List* arr[8];
} FCMemPool;
FCMemPool* FC_MemPoolAlloc();
void FC_MemPoolDestroy(void* pool);
void* FC_Malloc(void* pool, FC_size_t size);
void FC_Free(void* pool, void* ptr);
#endif // !__FC_MEM_MGR_H__
FCMemPool.c
#include "FCMemPool.h"
#include "list/list.h"
enum
{
FC_P_LVL_BASE = (FC_size_t)64,
FC_P_LVL_0 = (FC_size_t)(FC_P_LVL_BASE << 0),
FC_P_LVL_1 = (FC_size_t)(FC_P_LVL_BASE << 1),
FC_P_LVL_2 = (FC_size_t)(FC_P_LVL_BASE << 2),
FC_P_LVL_3 = (FC_size_t)(FC_P_LVL_BASE << 3),
FC_P_LVL_4 = (FC_size_t)(FC_P_LVL_BASE << 4),
FC_P_LVL_5 = (FC_size_t)(FC_P_LVL_BASE << 5),
FC_P_LVL_6 = (FC_size_t)(FC_P_LVL_BASE << 6), // 4096
FC_P_LVL_7 = (FC_size_t)(FC_P_LVL_BASE << 7), // 8192
};
static FC_size_t get_size(int lvl)
{
if (lvl >= 8 || lvl < 0)
{
return 0;
}
static FC_size_t size_arr[8] = {
FC_P_LVL_0, FC_P_LVL_1, FC_P_LVL_2, FC_P_LVL_3,
FC_P_LVL_4, FC_P_LVL_5, FC_P_LVL_6, FC_P_LVL_7};
return size_arr[lvl];
}
static int get_level(FC_size_t size)
{
if (size <= FC_P_LVL_0) { return 0; }
if (size <= FC_P_LVL_1) { return 1; }
if (size <= FC_P_LVL_2) { return 2; }
if (size <= FC_P_LVL_3) { return 3; }
if (size <= FC_P_LVL_4) { return 4; }
if (size <= FC_P_LVL_5) { return 5; }
if (size <= FC_P_LVL_6) { return 6; }
if (size <= FC_P_LVL_7) { return 7; }
return -1;
}
FCMemPool * FC_MemPoolAlloc()
{
FCMemPool * pool = (FCMemPool*)malloc(sizeof(FCMemPool));
int i = 0;
for (; i < 8; i++)
{
pool->arr[i] = FC_ListCreate();
}
return pool;
}
void FC_MemPoolDestroy(void * pool)
{
FCMemPool * p = (FCMemPool*)pool;
int i = 0;
for (; i < 8; i++)
{
FC_ListDestroy(p->arr[i]);
}
return;
}
void * FC_Malloc(void * pool, FC_size_t size)
{
if (pool == NULL || size <= 0)
{
return NULL;
}
int lvl = get_level(size);
if (lvl == -1)
{
return NULL;
}
FC_List* list = ((FCMemPool*)pool)->arr[lvl];
FC_Node* node = NULL;
if (list->available == 0)
{
node = FC_NodeCreate(get_size(lvl), lvl);
if (node == NULL)
{
return NULL;
}
FC_ListIncSize(list);
}
else
{
node = FC_ListPopHead(list);
if (node == NULL)
{
return NULL;
}
FC_ListDecAvailable(list);
}
return node->data;
}
void FC_Free(void * pool, void * ptr)
{
if (pool == NULL || ptr == NULL)
{
return;
}
FC_Node * node = FC_ListNode((char*)ptr);
FC_List * list = (FC_List*)(((FCMemPool*)pool)->arr[node->lvl]);
FC_ListAddHead(list, node);
FC_ListIncAvailable(list);
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "FC.h"
#include "FCMemPool/FCMemPool.h"
int main(int argc, char* argv[])
{
FCMemPool * p = FC_MemPoolAlloc();
char* bf = (char*)(FC_Malloc(p, 24));
FC_Free(p, bf);
FC_MemPoolDestroy(p);
return 0;
}