Você pode dar um passo atrás e ver de onde e por que esses modelos existentes vêm. Quando um processo é criado, é simplesmente fornecida uma área de armazenamento plana, que é simplesmente indexada de 0 a N. Como essa área de armazenamento (falando de RAM aqui) é suportada por um hardware dedicado e alguns semicondutores sofisticados, ela é bem rápida, mas não é o único desse tipo. Outros dispositivos, como discos rígidos, são essencialmente a mesma coisa, espaço plano endereçável por um índice, mas muitas ordens de magnitude mais lentas.
A razão pela qual "uma pilha" existe é porque seria impraticável para cada aplicativo tentar gerenciar o uso da RAM por si só. No passado, foi exatamente assim que aconteceu, os programadores planejaram com antecedência exatamente para que cada local de RAM seria usado. Como o software ficou mais complexo, alguém disse: não seria bom se eu pudesse ir a alguma caixa preta e dizer "Preciso de 10 bytes, então me dê" e não precisar me preocupar com todos os detalhes complexos de onde e como esses 10 bytes provêm ou como são recuperados. Isso é o que é um monte, realmente não fica mais básico do que isso.
Cada vez que um encadeamento é criado, existem algumas estruturas de dados (e uma pilha), que são adquiridas usando a mesma "operação gimme" que acabei de descrever. Uma pilha usada universalmente porque se encaixa perfeitamente com os quadros de pilha de chamadas de função e sua natureza LIFO. Em teoria, cada chamada de função e variáveis locais poderiam ser alocadas no heap, mas isso seria muito caro, comparado com apenas algumas instruções de montagem necessárias para atualizar o registro do ponteiro de pilha (ESP on x86).
O armazenamento local de encadeamento (TLS) também é construído sobre o heap. Quando um encadeamento é criado, como parte de uma viagem ao heap para alocar memória para estruturas de gerenciamento, um espaço separado para TLS também é alocado a partir do heap.
Portanto, no final, tudo o que você realmente tem é um alocador de memória genérico (ou seja, o heap) e todo o resto é uma forma especializada em cima disso. Em outras palavras, se você estiver disposto a desistir de algum aspecto de "Quero alocar o quanto quiser (ou tão pouco) quanto quiser), mantenha-o pelo tempo que desejar e livre sempre que quiser", você poderá fugir da negociação alocador de heap genérico para outro modelo que ofereça velocidade, mas ao custo de alguma outra limitação.
Pegue a pilha. É incrivelmente rápido quando comparado ao monte, mas as duas compensações são: 1) você não controla quando a memória é liberada; em vez disso, depois que a função terminar, o que quer que você tenha alocado se foi e 2) porque as pilhas geralmente têm tamanho limitado, você deve ter cuidado ao alocar grandes quantidades de dados diretamente na pilha.
Outro tipo de "modelo de memória" é o Virtual Memory Manager (VMM) oferecido por praticamente todos os principais sistemas operacionais por meio de chamadas do sistema. O VMM é muito semelhante ao heap, no sentido de que você pode solicitar qualquer quantidade de memória e mantê-la pelo tempo que desejar. No entanto, a limitação é que você só pode alocar memória em múltiplos de tamanho de página (por exemplo, 4KB), portanto, o uso direto do VMM causaria muita sobrecarga em um aplicativo típico que geralmente aloca de 8 a 24 bytes por vez. De fato, praticamente todas as implementações de heap são criadas no VMM especificamente para propósitos de permitir uma alocação de blocos pequenos muito genérica e não especializada . O heap vai para o VMM sempre que precisar de mais memória e distribui muitos pequenos pedaços dessa memória para o aplicativo.
Se você tiver um aplicativo que precise alocar blocos grandes, considere ir diretamente ao VMM, embora alguns heaps tenham uma instrução if dentro de malloc () e se o tamanho do bloco for maior que algum limite, eles simplesmente acessam o VMM para voce.
Outra forma de alocadores, em vez de usar diretamente heap, seria pools. Um pool é um alocador especializado em que todos os blocos são do mesmo tamanho. Pools (assim como a pilha e o TLS) são criados sobre o heap ou o VMM. Os pools são úteis em locais onde você aloca muitos (milhões) de pequenos objetos de vida curta e do mesmo tamanho. Pense em um serviço de rede processando solicitações recebidas. Cada solicitação de cliente pode resultar na alocação da mesma estrutura de N bytes para lidar com essa solicitação. O problema de usar pools é que cada pool lida apenas com um tamanho de bloco (mas você pode criar vários pools). A vantagem dos pools é que, como todos os objetos têm o mesmo tamanho, ele não requer lógica complexa. Em vez disso, sempre que você precisar de um novo bloco, ele fornecerá o que foi liberado recentemente.
E, por último, lembre-se da coisa que mencionamos no disco rígido. Você pode ter um modelo de memória que se comporte como um sistema de arquivos e duplique a mesma idéia de entradas de diretório e nós-i para permitir a alocação hierárquica de blocos de dados em que cada bloco de dados é endereçado com um caminho. É exatamente isso que o tmpfs faz.
Além das coisas que mencionei, tenho certeza de que existem outros modelos mais especializados, mas no final, já que tudo é baseado em espaço de endereço plano (isto é, até que alguns genuis surjam com algum tipo de espaço esquisito - um $$ não plano ), tudo remonta ao alocador genérico "gimme", que é o VMM ou o heap.