Com blocos de tamanho fixo, o que você descreveu é uma lista grátis . Essa é uma técnica muito comum, com o seguinte toque: a lista de blocos livres é armazenada nos próprios blocos livres. No código C, ficaria assim:
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;
}
Isso funciona bem desde que todos os blocos alocados tenham o mesmo tamanho e esse tamanho seja múltiplo do tamanho de um ponteiro, para que o alinhamento seja preservado. Alocação e desalocação são em tempo constante (ou seja, em tempo constante como a memória acessa e adições elementares - em um computador moderno, um acesso à memória pode envolver falhas de cache e até memória virtual, portanto, acessos ao disco, portanto, o "tempo constante" pode ser bem grande). Não há sobrecarga de memória (nenhum ponteiro extra por bloco ou coisas assim; os blocos alocados são contíguos). Além disso, o ponteiro de alocação atinge um determinado ponto apenas se, ao mesmo tempo, muitos blocos precisarem ser alocados: como a alocação prefere usar a lista livre, o ponteiro de alocação é aumentado apenas se o espaço abaixo do ponteiro atual estiver cheio. Nesse sentido, técnica.
Diminuindoo ponteiro de alocação após uma liberação pode ser mais complexo, pois os blocos livres podem ser identificados com segurança apenas seguindo a lista livre, que os percorre em ordem imprevisível. Se a diminuição do tamanho do segmento grande, quando possível, for importante para você, você pode usar uma técnica alternativa, com mais sobrecarga: entre dois blocos alocados, você coloca um "furo". Os orifícios são vinculados a uma lista duplamente vinculada, em ordem de memória. Você precisa de um formato de dados para um furo, para poder localizar o endereço inicial do furo, sabendo onde ele termina e também o tamanho do furo, se souber onde o furo começa na memória. Então, ao liberar um bloco, você cria um furo que mescla com os furos seguinte e anterior, reconstruindo (ainda em tempo constante) a lista ordenada de todos os furos. A sobrecarga é de cerca de duas palavras do tamanho de ponteiro por bloco alocado; mas, a esse preço, você pode detectar com segurança a ocorrência de um "furo final", ou seja, uma ocasião para diminuir o tamanho do grande segmento.
Existem muitas variações possíveis. Um bom artigo introdutório é a Alocação Dinâmica de Armazenamento: Uma Pesquisa e Revisão Crítica de Wilson et al.