Potresti voler fare un passo indietro e vedere da dove e perché provengono quei modelli esistenti. Quando viene creato un processo, viene semplicemente assegnata un'area di archiviazione piatta che viene semplicemente indicizzata da 0 a N. Poiché questa area di archiviazione (parlando di RAM qui) è supportata da un hardware dedicato e da alcuni semiconduttori fantasiosi sembra essere abbastanza veloce, ma non è l'unico nel suo genere. Altri dispositivi come i dischi rigidi sono essenzialmente la stessa cosa, spazio piatto indirizzabile da un indice, ma molti ordini di grandezza più lenti.
Il motivo per cui esiste un "heap" è perché non sarebbe pratico per ogni applicazione tentare di gestire l'utilizzo della RAM da solo. Nel passato, è esattamente così che è successo, i programmatori hanno pianificato in anticipo esattamente ciò per cui ogni posizione RAM sarebbe stata utilizzata. Man mano che il software diventa più complesso qualcuno ha detto, non sarebbe bello se potessi semplicemente andare in una scatola nera e dire "Ho bisogno di 10 byte così dammi" e non doversi preoccupare di tutti i dettagli intricati di dove e come quei 10 byte provengono o come vengono recuperati. Questo è ciò che è un heap, non diventa davvero più semplice di così.
Ogni volta che viene creato un thread, ci sono alcune strutture di dati (e uno stack), che vengono acquisite usando la stessa "operazione dammi" che ho appena descritto. Uno stack quasi universalmente usato perché si adatta perfettamente ai frame dello stack di chiamate di funzione e alla loro natura LIFO. In teoria, ogni invocazione di funzione e variabili locali potrebbero essere allocate sull'heap, ma sarebbe semplicemente troppo costoso, rispetto a poche istruzioni di assemblaggio necessarie per aggiornare il registro del puntatore dello stack (ESP su x86).
Anche l'archiviazione locale thread (TLS) è costruita in cima all'heap. Quando viene creato un thread, come parte di un viaggio nell'heap per allocare memoria per le strutture di gestione, viene allocato anche uno spazio separato per TLS dall'heap.
Quindi, alla fine, tutto ciò che hai davvero è un allocatore di memoria generico (cioè l'heap) e tutto il resto è un modulo specializzato. In altre parole, se sei disposto a rinunciare a qualche aspetto di "Voglio allocare tutto (o meno) quanto voglio, tienilo per tutto il tempo che voglio e libero quando voglio", potresti fare trading off allocatore di heap generico per un altro modello che offre velocità ma a costo di qualche altra limitazione.
Prendi lo stack. È incredibilmente veloce rispetto all'heap, ma i due compromessi sono 1) che non controlli quando la memoria viene liberata; invece, una volta terminata la funzione, qualunque cosa sia stata allocata scompare e 2) poiché le pile sono generalmente di dimensioni limitate, è necessario prestare molta attenzione nell'allocare grandi quantità di dati direttamente nello stack.
Un altro tipo di "modello di memoria" è il Virtual Memory Manager (VMM) offerto da quasi tutti i principali sistemi operativi tramite chiamate di sistema. VMM è molto simile all'heap, nel senso che puoi richiedere qualsiasi quantità di memoria e conservarla per tutto il tempo che desideri. Tuttavia, la limitazione è che è possibile allocare memoria solo in multipli di dimensioni di pagina (ad es. 4KB), quindi l'utilizzo diretto di VMM causerebbe un notevole sovraccarico in un'applicazione tipica che spesso alloca 8-24 byte alla volta. In effetti, quasi tutte le implementazioni di heap sono costruite su VMM appositamente allo scopo di consentire un'allocazione molto generica, non specializzata, di piccoli blocchi. Heap va su VMM ogni volta che necessita di più memoria e quindi distribuisce all'applicazione molti piccoli blocchi di tale memoria.
Se hai un'app, che ha bisogno di allocare blocchi di grandi dimensioni, potresti prendere in considerazione di andare direttamente a VMM, anche se alcuni heap hanno un'istruzione if all'interno di malloc () e se la dimensione del blocco è maggiore di qualche soglia, passa semplicemente a VMM per te.
Un'altra forma di allocatori invece di utilizzare direttamente l'heap, sarebbero i pool. Un pool è un allocatore specializzato in cui tutti i blocchi hanno le stesse dimensioni. I pool (proprio come stack e TLS) sono basati su heap o VMM. I pool sono utili in luoghi in cui si allocano molti (milioni) oggetti piccoli e di breve durata della stessa dimensione. Pensa a un servizio di rete che elabora le richieste in arrivo. Ogni richiesta client può comportare l'allocazione della stessa struttura N byte per gestire quella richiesta. Il compromesso con l'utilizzo di pool è che ogni pool gestisce solo una dimensione di blocco (ma è possibile creare più pool). Il vantaggio dei pool è che, poiché tutti gli oggetti hanno le stesse dimensioni, non richiede una logica complessa. Invece, ogni volta che hai bisogno di un nuovo blocco, ti dà solo quello che è stato recentemente liberato.
E infine, ricorda quella cosa sul disco rigido che ho citato in alto. Potresti avere un modello di memoria che si comporta come un file system e duplica la stessa idea di voci di directory e i-nodi per consentire l'allocazione gerarchica dei blocchi di dati in cui ciascun blocco di dati viene indirizzato con un percorso. Questo è esattamente ciò che fa tmpfs .
Oltre alle cose che ho citato, sono sicuro che ci sono altri modelli più specializzati, ma alla fine poiché tutto è basato sullo spazio di indirizzamento piatto (cioè fino a quando alcuni genuis non escono con una sorta di spazio strano-a $$ non piatto ), tutto risale all'allocatore "gimme" generico che è VMM o heap.