对于固定大小的块,您所描述的是一个空闲列表。这是一种非常常见的技术,但有以下不同之处:空闲块列表存储在空闲块本身中。在C代码中,它看起来像这样:
static void *alloc_ptr = START_OF_BIG_SEGMENT;
static void *free_list_head = NULL;
static void *
allocate(void)
{
void *x;
if (free_list_head == NULL) {
x = alloc_ptr;
alloc_ptr = (char *)alloc_ptr + SIZE_OF_BLOCK;
} else {
x = free_list_head;
free_list_head = *(void **)free_list_head;
}
return x;
}
static void
release(void *x)
{
*(void **)x = free_list_head;
free_list_head = x;
}
只要所有分配的块具有相同的大小,并且该大小是指针大小的倍数,此方法就可以很好地保持对齐。分配和释放是固定时间的(即与内存访问和基本添加相同的恒定时间),在现代计算机中,内存访问可能涉及高速缓存未命中甚至虚拟内存,因此涉及磁盘访问,因此“恒定时间”可能会很大)。没有内存开销(没有额外的每个块指针或类似的东西;分配的块是连续的)。同样,仅在一次必须分配许多块的情况下,分配指针才会到达给定点:由于分配倾向于使用空闲列表,因此仅当当前指针下方的空间已满时,分配指针才会增加。从这个意义上讲 技术。
减少释放后的分配指针可能会更加复杂,因为只有遵循空闲列表(以不可预测的顺序遍历它们)才能可靠地标识空闲块。如果在可能的情况下减小大片段的大小对您很重要,那么您可能希望使用另一种技术,但会增加开销:在任何两个已分配的块之间放置一个“孔”。这些孔与一个双链表按内存顺序链接在一起。您需要孔的数据格式,以便可以通过知道孔的结束位置来确定孔的起始地址,如果知道孔在内存中的起始位置,还可以找到孔的大小。然后,当释放一个块时,您将创建一个孔,该孔将与下一个孔和上一个孔合并,从而重建(仍在恒定时间内)所有孔的有序列表。那么,每个分配的块的开销大约是两个指针大小的字。但是,以该价格,您可以可靠地检测到“最终孔”的发生,即减小大段尺寸的机会。
有许多可能的变化。很好的介绍性论文是《动态存储分配: Wilson 等人的调查和重要评论》。