基于内存池的空间配置器

一、设计思路

      内存池分两部分,一部分是内存池列表,一部分是没有分割的原始连续内存。

1 内存池列表

      内存池由16个列表组成,每个列表维护大小相同的内存块,内存块的大小是8的倍数。最小的内存块是8字节,最大的内存块是128字节。节点结构图如下:

-------     -------    -------    ------    -----     ------
| 8    |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------
-------     -------    -------    ------    -----     ------
| 16  |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------
-------     -------    -------    ------    -----     ------
| 24  |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------
-------     -------    -------    ------    -----     ------
| 56  |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------
-------     -------    -------    ------    -----     ------
| 64  |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------

…………………………
-------     -------    -------    ------    -----     ------
| 120 |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------
-------     -------    -------    ------    -----     ------
| 128 |--->|     |--->|     |--->|    |--->|    |--->|    |
-------     -------    -------    ------    -----     ------

 

内存池节点结构:

 

union free_list
{
      free_list* next;  


};

 

每个内存块列表的头指针由下面数组维护,其实是一个指针数组,每一个数组元素存放列表的首地址。

static free_list* head_list[POOL_SIZE];

 

2)原生连续内存

       为了提高性能,每次向os申请内存时,会预先分配较大的内存。给内存块列表分配一部分后,剩余的则保存下来。

      //连续内存的起始地址

      static char* start_free;

 

      //连续内存的结尾地址
     static char* end_free;

 

//内存池向os申请内存的总字节数

static size_t heap_size;

 

连续内存块表示:

    -------------------------------------------------------------

   |                                                                            |

   --------------------------------------------------------------

   ^ start_free                                                         ^ end_free

 

3)内存分配策略

叙述之前做一下约定:

req_size ,表示用户请求的内存大小。

 

_round_up(size_t size),表示对用户请求的内存大小,向上调整为8的倍数。

例如:req_size = 14 , 那么_round_up(req_size ) 的返回值是16

 

_pool_watermark(size_t size),表示用户请求的内存在head_list中的下标。

例如:req_size = 14 , 那么_pool_watermark(req_size ) 的返回值是1 

 

具体策略:
1
如果用户申请超过128字节的内存,则直接调用简单空间配置器;否则,执行2)。

2)调用_round_up(req_size ),然后到head_list[_pool_watermark(req_size)中获取内存。

    如果获取成功,返回给用户。否则,执行3)。

3)我们预先会向原生连续内存块申请 20 *_round_up(req_size ) 字节大小的内存,如果原生内存池能满足要求,则返回给客户;否则,如果原生内存剩下的内存大于或者等于req_size,则返回给客户;否则,执行4

4)我们会向os申请 2 * 20 *_round_up(req_size ) + (heap_siz >> 4)大小的内存,如果成功了返回客户。否则调用“简单空间配置器”去申请内存。

4)内存释放策略

如果释放的内存超过128字节,则调用“简单空间配置器”的内存释放函数。否则,释放当相应的内存块列表中。

 

二、源代码

 

 #ifndef _BBG_ALLOCPOOL_H_ #define _BBG_ALLOCPOOL_H_ /* 内存池,维护16个小的内存快链表,每个链表节点的大小是8的倍数。 ------- ------- ------- ------ ----- ------ | 8 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ ------- ------- ------- ------ ----- ------ | 16 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ ------- ------- ------- ------ ----- ------ | 24 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ ------- ------- ------- ------ ----- ------ | 56 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ ------- ------- ------- ------ ----- ------ | 64 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ ------- ------- ------- ------ ----- ------ | 120 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ ------- ------- ------- ------ ----- ------ | 128 |--->| |--->| |--->| |--->| |--->| | ------- ------- ------- ------ ----- ------ */ #include <cstdlib> #include "../bbg_common/bbg_common.h" #include "bbg_simple_alloc.h" BEGIN_BBG #define BLOCK_OFFSET 3 #define BLOCK_ALLIGN (1 << BLOCK_OFFSET) #define MAX_BLOCK_SIZE 128 #define POOL_SIZE (MAX_BLOCK_SIZE >> BLOCK_OFFSET) #define POOL_HIGH_WATERMARK (POOL_SIZE - 1) class alloc_pool { public: union free_list { free_list* next; }; static void* allocate(size_t size) { return (MAX_BLOCK_SIZE < size)? simple_alloc::allocate(size) : _get_from_pool(size); } static void deallocate(void* pointer , size_t size) { if ( MAX_BLOCK_SIZE < size ) { simple_alloc::deallocate(pointer , size); return; } //释放到内存池 _dealloc_pool(pointer , size); } private: static void _dealloc_pool(void* pointer , size_t size) { //将size向上取整为8的倍数 size_t round_up_size = _round_up(size); int watermark = _pool_watermark(round_up_size); ((free_list*)pointer)->next = head_list[watermark]; head_list[watermark] = (free_list*)pointer; } static void* _get_from_pool(size_t size) { //将size向上取整为8的倍数 size_t round_up_size = _round_up(size); int watermark = _pool_watermark(round_up_size); //看看内存池中是否有合适当前大小的内存快,如果有,则直接反给用户 free_list* list_head = head_list[watermark]; if (list_head) { head_list[watermark] = list_head->next; return (void*)list_head; } //从更大一级内存快的内存池里面取。 return _refill(size); } static void* _refill(size_t size) { //将size向上取整为8的倍数 size_t round_up_size = _round_up(size); int needWatermark = _pool_watermark(round_up_size); //先从内存池中,预先获取20块。 int nblocks = 20; char* get_bytes = _chunk_alloc(round_up_size , nblocks); while ( nblocks-- ) { ((free_list*)get_bytes)->next = head_list[needWatermark]; head_list[needWatermark] = (free_list*)get_bytes; get_bytes += round_up_size; } void* result = (void*)head_list[needWatermark]; head_list[needWatermark] = head_list[needWatermark]->next; return result; } static char* _chunk_alloc(size_t size , int& nblocks) { char* result = 0; size_t needBytes = size * nblocks; size_t leftBytes = end_free - start_free; //内存池中剩余的内存足够客户的需要,那么直接返回 if ( leftBytes > needBytes) { result = start_free; start_free += needBytes; return result; } //此时,内存池中剩余的内存,不足以满足 size * nblocks大小的需求,那么看看是否满足size大小 // 如果是,那么直接返回 if ( leftBytes > size ) { nblocks = (int)(leftBytes / size); result = start_free; start_free += nblocks * size; return result; } //到了这里,那么内存池中的剩余内存,无法满足用户需求了,那么重新向os索取内存了。 //索取之前,需要把池中剩余不足size的内存,保存在链表中。 if ( leftBytes > 0 ) { int watermark = _pool_watermark(leftBytes); ((free_list*)start_free)->next = head_list[watermark]; head_list[watermark] = (free_list*)start_free; } //向os索取内存 size_t reAllocBytes = (needBytes << 1) + _round_up(heap_size >> 4); start_free = (char*)malloc(reAllocBytes); if ( 0 == start_free ) { //这里都分配失败了,调用简单配置器,因为他有异常处理例程 start_free = (char*)simple_alloc::allocate(reAllocBytes); } heap_size += reAllocBytes; end_free = start_free + reAllocBytes; return _chunk_alloc(size , nblocks); } static size_t _round_up(size_t size) { return (size + BLOCK_ALLIGN - 1) & ~(BLOCK_ALLIGN-1); } static int _pool_watermark(size_t size) { return (int)((size >> BLOCK_OFFSET) - 1); } private: static free_list* head_list[POOL_SIZE]; static char* start_free; static char* end_free; static size_t heap_size; }; char* alloc_pool::start_free = 0; char* alloc_pool::end_free = 0; size_t alloc_pool::heap_size = 0; //初始化全局内存池变量 alloc_pool::free_list* alloc_pool::head_list[POOL_SIZE] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0, 0 }; END_BBG// end of name space bbg #endif//_BBG_ALLOCPOOL_H_

你可能感兴趣的:(list,OS,Class,UP)