这里是对https://github.com/zouxiaohang/TinySTL中的alloc的进行解析
下面这个仓库写的真好,文中的很多信息都是来自于该仓库
参考:https://github.com/steveLauwh/SGI-STL
解析stl是一种对艺术的享受
在计算机的发展中,内存一直是最重要的三个部分之一,而内存面临的主要问题,就在于如何进行高效的分配,以及内存碎片化问题,为了在一定程度上解决这些问题,stl中提供了一种方案,这就是空间配置器
空间配置器,分为两级,
- 当配置区块超过 128 bytes,就使用第一级配置器
- 当配置区块小于 128 bytes,使用内存池管理
空间配置器包含,动态空间配置、空间管理、空间释放
那么这两级是如何设计的呢?
void *alloc::allocate(size_t bytes){
// EMaxBytes::MAXBYTES = 128
/*
这里大于128,就直接调用malloc
*/
if (bytes > EMaxBytes::MAXBYTES){
return malloc(bytes);
}
// 小于128,
/*
// 根据区块大小,决定使用第n号free-list, n从0开始计算
该函数用来计算当前的链表节点序号
static size_t FREELIST_INDEX(size_t bytes) {
// return ((bytes + 8 - 1)/8 - 1);
return ((bytes + EAlign::ALIGN - 1)/EAlign::ALIGN - 1);
}
对于碎片化的内存,配置器中使用的是链表来存储可用的空间
可以看下面的图片,可以更好的理解存储过程
*/
size_t index = FREELIST_INDEX(bytes);
obj *list = free_list[index];
if (list){//此list还有空间给我们
free_list[index] = list->next;
return list;
}
else{
//此list没有足够的空间,需要从内存池里面取空间
/*
// 将bytes 上调至8的倍数
static size_t ROUND_UP(size_t bytes) {
return ((bytes + EAlign::ALIGN - 1) & ~(EAlign::ALIGN - 1));
}
*/
return refill(ROUND_UP(bytes));
}
}
free-list 是指针数组,16 个数组元素,就是 16 个 free-list,各自管理大小分别为 8, 16, 24, 32,…128 bytes(8 的倍数)的小额区块。
小额区块的结构体 union _Obj 使用链表连接起来
用到的变量声明
static char *start_free;//内存池起始位置
static char *end_free;//内存池结束位置
char *alloc::start_free = 0;
char *alloc::end_free = 0;
size_t alloc::heap_size = 0;
alloc::obj *alloc::free_list[alloc::ENFreeLists::NFREELISTS] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
//free-lists的节点构造
union obj{
union obj *next;
char client[1];
};
static obj *free_list[ENFreeLists::NFREELISTS];
如果在链表中有剩余的空间,那么就直接取用,但如果没有足够的剩余呢?
那就要调用一下下面这个函数
// 返回一个大小为n的对象,并且有时候会为适当的free_list增加节点
// 假设bytes已经上调为8的倍数
void* alloc::refill(size_t bytes) {
// ENObjs::NOBJS = 20;
size_t nobjs = ENObjs::NOBJS;
// 该函数的解析在下面
char *chunk = chunk_alloc(bytes, nobjs);
obj **my_free_list = 0;
obj *result = 0;
obj *current_obj = 0, *next_obj = 0;
if (nobjs == 1) {
// 取出空间只够一个对象使用
return chunk;
} else {
my_free_list = free_list + FREELIST_INDEX(bytes);
result = (obj*)(chunk);
*my_free_list = next_obj = (obj*)(chunk + bytes);
// 将取出多余的空间加入到相应的free-list里面去
for (int i = 1; ; ++i) {
current_obj = next_obj;
next_obj = (obj*)((char*)next_obj + bytes);
if (nobjs - 1 == i) {
current_obj->next = 0;
break;
} else {
current_obj->next = next_obj;
}
}
return result;
}
}
//假设bytes已经上调为8的倍
char *alloc::chunk_alloc(size_t bytes, size_t& nobjs){
char *result = 0;
// 需要的块大小和相应的块数目
size_t total_bytes = bytes * nobjs; // 需要申请的空间大小
// 通过start_free 和 end_free , 初始状态下两者都是0,说明空间为空
size_t bytes_left = end_free - start_free; // 计算内存池剩余空间
if (bytes_left >= total_bytes){
// 内存池剩余空间完全满足需要
result = start_free;
// 开始指针移动,表示该段空间已使用
start_free = start_free + total_bytes;
return result;
} else if (bytes_left >= bytes){
// 内存池剩余空间不能完全满足需要,但足够供应一个或以上的区块
// 计算可以供给的内存块数目,这里的两步是为了计算8的整数块
nobjs = bytes_left / bytes;
total_bytes = nobjs * bytes;
result = start_free;
// 表明空间已使用
start_free += total_bytes;
return result;
} else {
// 内存池剩余空间连一个区块的大小都无法提供
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
// 内存池的剩余空间分给合适的空闲链表
if (bytes_left > 0){
// 将剩余的内存块,插入到合适大小的内存块链表中
obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
((obj *)start_free)->next = *my_free_list;
*my_free_list = (obj *)start_free;
}
// 配置heap空间,用来补充内存池
start_free = (char *)malloc(bytes_to_get);
if (!start_free){
// heap 空间不足,malloc 失败
obj **my_free_list = 0, *p = 0;
// 0 ~ 128 , 8
for (int i = 0; i <= EMaxBytes::MAXBYTES; i += EAlign::ALIGN){
// 遍历空闲链表
/*
因为之前的分配要的太多了,所以就从更小内存块申请
*/
my_free_list = free_list + FREELIST_INDEX(i);
p = *my_free_list;
if (p != 0){
// 空闲链表节点有效
*my_free_list = p->next;
// 单个内存块大小
start_free = (char *)p;
end_free = start_free + i;
return chunk_alloc(bytes, nobjs);
}
}
// 内存空间清零
end_free = 0;
}
// 堆大小
heap_size += bytes_to_get;
// 扩展空间
end_free = start_free + bytes_to_get;
return chunk_alloc(bytes, nobjs);
}
}
释放内存
void alloc::deallocate(void *ptr, size_t bytes){
// > 128, 释放由malloc分配的内存
if (bytes > EMaxBytes::MAXBYTES){
// 释放申请的大块内存
free(ptr);
}
else{
// 碎片内存,重新归入内存池
size_t index = FREELIST_INDEX(bytes);
obj *node = static_cast<obj *>(ptr);
node->next = free_list[index];
free_list[index] = node;
}
}
// 重新分配,释放旧大小,申请新的大小
void *alloc::reallocate(void *ptr, size_t old_sz, size_t new_sz){
deallocate(ptr, old_sz);
ptr = allocate(new_sz);
return ptr;
}