一、设计思路
内存池分两部分,一部分是内存池列表,一部分是没有分割的原始连续内存。
(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字节,则调用“简单空间配置器”的内存释放函数。否则,释放当相应的内存块列表中。
二、源代码