一级空间配置器
当所申请的空间大于128bytes时,stl直接使用一级空间配置器
一级空间配置器其实是对malloc的封装,以及添加了类似new_handler处理内存分配不足的情况
当内存不足时调用oom_malloc尝试回收一些已分配的内存,调用的是__malloc_alloc_oom_handler方法(如果有的话,没有就抛出异常)
1 | template<int inst> |
二级空间配置器
二级空间配置器其实是一个内存池,他维持这一个空闲链表数组,所需字节数全上调成8的倍数,然后在空闲链表里面找。
当空闲链表内存块不足时,优先从已申请好的空闲堆内存中填充空闲链表1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21enum{__ALIGN = 8 };
enum{__MAX_BYTES = 128};
enum{__NFREELIST = __MAX_BYTES/__ALIGN};
union obj //链表的结构,空间的有效利用
{
union obj *free_list_link;
char client_data[1];
};
static obj * volatile free_list[__NFREELIST];//空闲链表数组
static size_t FREELIST_INDEX(size_t bytes)
{
return (bytes + __ALIGN -1) / __ALIGN -1;//寻找在数组中的下标位置
}
static size_t ROUND_UP(size_t bytes)
{
return (bytes + __ALIGN -1) & ~(__ALIGN-1);//上调成8的倍数
}
static char * start_free; //空闲内存起始地址
static char * end_free; //空闲内存结束地址
static size_t heap_size; //空闲堆内存大小
allocate
如果找到了则把那块空间从free_list中非配给用户,没找到则调用refill()填充free_list1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18static void * allocate(size_t n)// 15
{
obj * volatile * my_free_list = NULL;
obj *result = NULL;
if(n > (size_t) __MAX_BYTES)
{
return malloc_alloc::allocate(n);
}
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if(NULL == result) //如果free_list为空则调用refill填充或直接分配
{
void *r = refill(ROUND_UP(n));
return r;
}
*my_free_list = result->free_list_link;
return result;
}
refill
STL规定refill为内存不足的那块填充20块相应大小的内存块,但是可能出现内存不足的情况所有申请到的不一定是20个内存快(可能只有1个)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28static void * refill(size_t n) //
{
int nobjs = 20;
char *chunk = chunk_alloc(n,nobjs);// 申请内存块
obj *volatile* my_free_list;
obj *result;
obj *current_obj, *next_obj;
int i;
if(1==nobjs) return chunk;
my_free_list=free_list + FREELIST_INDEX(n);
result=(obj *)chunk;
*my_free_list=next_obj=(obj *)(chunk+n);
for(i=1;;i++) //将剩余的块连接到空闲链表
{
current_obj = next_obj;
next_obj=(obj *)((char *)next_obj + n);
if(nobjs -1==i)
{
current_obj->free_list_link=0;
break;
}
else
{
current_obj->free_list_link=next_obj;
}
}
return result; //将第一个内存块返回
}
chunk_alloc
chunk_alloc函数分配内存块给free_list,他和refill是维持这个结构的核心。处理了多种内存不足的情况1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56static char * chunk_alloc(size_t size,int &nobjs)
{
char *result = NULL;
size_t total_bytes = size*nobjs;
size_t bytes_left = end_free - start_free;
if(bytes_left >= total_bytes)//当剩余内存完全足够
{
result = start_free;
start_free += total_bytes;
return result;
}else if(bytes_left >= size)//内存不够分配20块但能分配一块或一块以上
{
nobjs = bytes_left/size;
total_bytes = size*nobjs;
result = start_free;
start_free += total_bytes;
return result;
}else //剩余内存一块都不能分配
{
size_t bytes_to_get=2 *total_bytes+ROUND_UP(heap_size>>4);
//先给剩余的内存找到合适的free list(先处理start_free——end_free的那段内存)
if(bytes_left>0)
{
obj * volatile *my_free_list=free_list +FREELIST_INDEX(bytes_left);
((obj *)start_free)->free_list_link= *my_free_list;
*my_free_list=(obj *)start_free;
}
//向堆空间申请内存
start_free=(char *)malloc(bytes_to_get);
if(0==start_free)//堆空间不足时
{
int i;
obj *volatile* my_free_list,*p;
//遍历比他大的free list查看是否还剩有未使用内存
for(i=size;i<__MAX_BYTES;i+=__ALIGN)
{
my_free_list = free_list +FREELIST_INDEX(i);
p = *my_free_list;
if(0!=p)//
{
*my_free_list = p ->free_list_link;
start _free = (char *)p;
end_free = (char *)p;
//由于nobjs 传的是引用所以递归调用修正nobjs
return (chunk_alloc(size,nobjs));
}
}
end_free=0;
//调用一级空间配置器,看看malloc_handler能否挤出内存...
start_free =(char *)malloc_alloc::allocate(bytes_to_get);
}
heap_size +=bytes_to_get;
end_free =start_free +bytes_to_get;
return (chunk_alloc(size,nobjs));//同上
}
}
释放内存
1 | static void deallocate(void *p,size_t n) |
结构图
如图:
参考资料——STL源码剖析