Il tuo computer non ha solo RAM. Ha un panorama di memoria disordinato, frammentato e in costante cambiamento che la CPU finge essere infinito. Questa è la realtà delle strutture dati dinamiche eseguite su hardware moderno.
Una tipica workstation oggi ha da 16 a 64 megabyte di RAM fisica. Ma non lasciarti ingannare da questi piccoli numeri. Attraverso una tecnica chiamata memoria virtuale, il sistema scambia i dati tra quella RAM e il disco rigido. La CPU vede un’illusione. Pensa di avere da 200 a 500 megabyte di spazio contiguo. L’illusione funziona. Il codice viene eseguito. Ma quando il sistema operativo deve scavare nel disco rigido per recuperare una pagina di memoria, tutto rallenta. Lo senti nel ritardo. Lo senti nel movimento della ventola. Nonostante il calo delle prestazioni, la memoria virtuale è un modo economico per “espandere” la RAM. È un compromesso necessario.
Supponiamo di fare tabula rasa. Uno spazio di memoria totale di 50 megabyte. Non più. Niente di meno. Questo è il bilancio.
Il sistema operativo possiede questo blocco da 50 megabyte. Divide la torta. Non allo stesso modo. Mai allo stesso modo.
Innanzitutto c’è il codice. Le istruzioni eseguibili per ogni applicazione attualmente in esecuzione, oltre al kernel del sistema operativo stesso. Questa sezione è statica. Non cambia mentre il programma è vivo. Poi arrivano le variabili globali. Queste sono le costanti condivise e i flag di stato che ogni parte dell’applicazione può toccare. Rimangono in memoria dal momento in cui l’app viene avviata fino alla sua morte.
Poi c’è la pila.
La pila è rigida. Cresce e si restringe di pari passo con le chiamate di funzione. Quando invochi una funzione, le sue variabili locali e i suoi parametri vengono inseriti nello stack. Quando la funzione ritorna, vengono eliminati. Lo stack ricorda l’ordine di chiamata. Ti assicura di tornare nel posto giusto. Ma ha una dimensione massima. Non è possibile chiedere allo stack più memoria durante il volo. Se ci provi, lo stack trabocca. Il programma si blocca.
Al termine di un programma, il sistema operativo lo scarica. Il codice, i valori globali, lo spazio dello stack: tutto è stato cancellato. Quella memoria viene riciclata. È pronto per il prossimo programma.
Ma ecco il problema. In qualsiasi momento, circa il 50% di quello spazio da 50 megabyte potrebbe essere inutilizzato. Perché? Perché lo stack contiene solo ciò che è attualmente in esecuzione. Il codice contiene solo le istruzioni. I pezzi di memoria inutilizzati vengono sparsi. Sono buchi.
Il sistema operativo prende questi buchi e li raggruppa insieme. Chiama questo pool collettivo il mucchio.
È qui che avviene l’allocazione dinamica. L’heap è l’unico posto nella memoria in cui un programma può richiedere un’esatta quantità di spazio, proprio quando ne ha bisogno. Non dichiari un array di 1000 numeri interi in fase di compilazione. Non sai se te ne serviranno 1.000 o 100.000. Aspetti. Esegui il codice. Decidi che hai bisogno di quello spazio. Quindi chiami “malloc”.
“malloc” sta per allocare memoria. Prende un blocco dal mucchio. Restituisce un puntatore. Usi quel puntatore. Quando hai finito, chiami “libero”. Il blocco ritorna in piscina.
Questa è la differenza fondamentale tra memoria statica e dinamica. Lo stack è automatico. È legato all’ambito. L’heap è manuale. È legato all’intento.
Perché questo è importante per te? Perché la maggior parte dei bug nei software complessi non deriva da una cattiva logica. Derivano da una cattiva gestione della memoria. Se si alloca dall’heap e si dimentica di liberarlo, si perde memoria. Il mucchio si restringe. Alla fine, il sistema esaurisce lo spazio. I programmi si bloccano. Il sistema operativo interrompe i processi. Perdi il lavoro non salvato.
Al contrario, se provi ad allocare troppo nello stack, superi il limite dello stack. Il programma si interrompe bruscamente.
L’heap consente flessibilità. Consente strutture di dati che crescono. Elenchi collegati. Alberi. Grafici. Nessuno di questi può essere definito in fase di compilazione con una dimensione fissa. Devono essere costruiti al volo. Devono vivere nel mucchio.
Il mucchio è perfetto? No. Si frammenta. Man mano che assegni e liberi blocchi di dimensioni diverse, l’heap diventa un puzzle con pezzi mancanti. La ricerca di un blocco contiguo di memoria libera può diventare lenta. Questo è il motivo per cui “malloc” a volte può richiedere una notevole quantità di tempo. Deve cercare. Deve coalizzarsi. Deve gestire il caos.
Ma senza l’heap, l’informatica moderna crolla.


























