前言
在本小节中我们将讲解chunk_alloc
函数,从内存池中取空间给free_list,这便是chunk_alloc
需要做的事。
chunk_alloc
引入
该函数的大致逻辑是:
若内存池的容量够需求,则直接返回给free_list;如果不够,能分多少就分多少给free_list;如果一个都分不了,则看看内存池还有没有剩余的零头,如果有的话,分给其他free_list,然后对内存池进行补充容量(malloc申请),如果申请失败,只好依靠没有用的且足够大的free_list了,最后实在哪里都没有内存可以拿出来了,只能调用第一级配置器,看看oom机制能否坚持到有内存可用。
深入源码
/* We allocate memory in large chunks in order to avoid fragmenting */
/* the malloc heap too much. */
/* We assume that size is properly aligned. */
/* We hold the allocation lock. */
template <bool threads, int inst>
char*
__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;
/* 若容量够total_bytes,直接分配
* 否则能分配几个节点分配几个
* 一个都不能满足,那把内存池剩余的空间分给其他的free_list,并重新填充内存池
*/
if (bytes_left >= total_bytes) {
result = start_free;
start_free += total_bytes;
return(result);
} else if (bytes_left >= size) {
//nobjs传入的是引用,所以此时会变成实际上能满足的节点个数
nobjs = bytes_left/size;
total_bytes = size * nobjs;
result = start_free;
start_free += total_bytes;
return(result);
} else {
/* bytes_to_get代表要申请的容量
* 注意是总容量的2倍加附加量,分total_bytes给free_list,剩下的留给内存池
*/
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
// Try to make use of the left-over piece.
//尝试将剩余的空间分给满足的free_list
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;
// 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了
* 足够大小是指,至少能满足一个free_list块的大小(因为调用的递归,而递归的出口就满足total_bytes或者满足一个节点)
*/
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机制了
* 因为之前malloc都没成功,所以第一级去malloc多半也会失败,好在第一级配置器有个oom机制
* 最后实在不行,会抛出bad_alloc异常
*/
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.
}
/* 走到这一步证明已经突破了重重困难将容量申请成功了
* 调整heap_size及end_free
* 最后再次调用chunk_alloc,将容量分配给free_list
*/
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
return(chunk_alloc(size, nobjs));
}
}
小结
以上便是chunk_alloc
的做法,需要特别注意的一点是填充内存池的时候,申请的容量是free_list需要的容量的2倍加上附加量,这点和vector
容器很像。之后将需要的容量返回,剩下的留给内存池,供下次调用chunk_alloc
时使用。
接下来我们将整理一下空间配置器整体的知识。