SGISTL源码阅读三 空间配置器下(内存池memory pool)
前言
在上一个博客我们讲述了空间配置器的第二级配置器,它的关键点free-lists是依赖于内存池的。在refill
中我们通过调用chunk_alloc
函数来申请区块,chunk_alloc
的作用就是从内存池中取空间给free-lists使用。下面我们通过源码来解读它。
深入源码
template <bool threads, int inst>
char*
//注意,此处nobjs传的是引用
__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{
char * result;
size_t total_bytes = size * nobjs;
size_t bytes_left = end_free - start_free;
//如果剩余空间大于所申请的空间(nobjs个size大小)
if (bytes_left >= total_bytes) {
//调整内存池的起始地址后返回result
result = start_free;
start_free += total_bytes;
return(result);
//如果剩余空间大于一个size
} else if (bytes_left >= size) {
//调整nobjs
nobjs = bytes_left/size;
total_bytes = size * nobjs;
result = start_free;
//调整内存池的起始地址,将内存池中所有的空间都返回给free-lists使用
start_free += total_bytes;
return(result);
//内存池中的空间不够了(重点)
} else {
//这里我们可以看到内存池向内存所申请的空间是total_bytes的两倍+附加量
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
// 如果内存池不为空,将其置为空(把所有容量都供free-lists使用)
if (bytes_left > 0) {
//找到对应的free-list并添加进去
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;
// Try to make do with what we have. That can't
// hurt. We do not try smaller requests, since that tends
// to result in disaster on multi-process machines.
//从区块大小更大的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 = start_free + i;
return(chunk_alloc(size, nobjs));
// Any leftover piece will eventually make it to the
// right free list.
}
}
//如果失败了,就只能调用第一级配置器,从oom中寻求解决方案
//(如果oom机制也没有解决,那么会抛出一个异常,详见第一级配置器https://blog.csdn.net/lyn_00/article/details/83929578)
end_free = 0; // In case of exception.
start_free = (char *)malloc_alloc::allocate(bytes_to_get);
// This should either throw an
// exception or remedy the situation. Thus we assume it
// succeeded.
}
//走到这里,说明已经成功申请了total_bytes的两倍+附加量的空间
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
//最后递归调用自己,修正nobjs的值
return(chunk_alloc(size, nobjs));
}
}
简要描述一下chunk_alloc
的机制
1.如果内存池中的容量足够那么直接将total_bytes(size*nobjs)的空间供free-lists使用。
2.如果内存池中的容量没那么多,但是超过一个size大小,就改变nobjs的值,将内存池中所有的空间给free-lists使用。
3.如果内存池中的容量连一个size大小都没有(这种情况是最复杂的)
(1)将内存池中的容量置零
(2)申请total_bytes的两倍+附加量这么大的空间,如果申请成功了,递归调用它本身,将申请到的容量一半给free-lists,一半存留在内存池中,如果申请失败,见(3)
(3)在free-lists中寻找更大的free-list收回至内存池,再递归调用它本身。如果失败,见(4)
(4)这种情况就是最坏的情况了,我们把它交给第一级配置器的oom机制去处理,最坏的结果是抛出异常。
总结
之前在学习第二级配置器的时候有一个疑惑,现在就清楚了,内存池中处理了申请不到内存的情况。
现在我们对SGISTL的配置器应该也有了一个比较深入的理解。