Perché abbiamo bisogno di un heap se tutto può essere fatto in modo molto più efficiente in pila?


24

Questo in realtà è in qualche modo correlato alla domanda che ho posto ieri sul perché sia ​​uno Stack che un Heap sono necessari nelle applicazioni che usiamo oggi (e perché non possiamo semplicemente andare con un Heap invece di entrambi, al fine di avere un semplice & standard singolare da seguire).

Tuttavia, molte delle risposte hanno indicato che uno Stack è insostituibile a causa del fatto che è molte centinaia (o migliaia) volte più veloce rispetto al tentativo di allocare / fare riferimento all'Heap. So che c'è un problema con l'allocazione dinamica della memoria se eliminiamo l'heap, ma non c'è un modo per aggirare questo, o forse, un modo per migliorare sullo stack in modo che possa gestire l'allocazione dinamica della memoria?


4
Due estratti della tua domanda precedente: "il rovescio della medaglia più importante è che ha uno spazio limitato e quindi contenere oggetti di grandi dimensioni o cercare di usarlo per oggetti di lunga durata, sono entrambe cattive idee" e "le pile sono estremamente efficienti struttura per la gestione dei dati che obbedisce alle regole LIFO (last in first out) ".
Cascabel,

2
La tua premessa è errata - non tutto può essere fatto in modo molto più efficiente in pila. Ciò non è in contraddizione con le risposte che hai ricevuto - che ciò che può essere fatto in pila può essere fatto molto più velocemente lì.
Ingo,

... supponendo che il tuo hardware abbia uno stack o un indirizzamento relativo allo stack.
Ritch Melton,

3
Sono convinto. Lo dico io.
JeffO,

Risposte:


25

Il problema con gli stack è che non è possibile "liberare" la memoria a meno che non sia in cima allo stack. Ad esempio, supponi di aver assegnato 3 elementi di dimensioni diverse:

a = allocate(2000000); // 2000000 bytes
b = allocate(1);
c = allocate(5000000);

La pila avrebbe asul fondo, bnel mezzo e cin cima. Questo diventa problematico se vogliamo liberare b:

free(b); // b is not on top! We have to wait until c is freed!

La soluzione alternativa è spostare tutti i dati dopo be spostarli in modo che vengano dopo a. Funziona, ma richiederà 5000000 copie in questo caso - qualcosa che sarà molto più lento di un heap.

Questo è il motivo per cui abbiamo un mucchio. Mentre l'allocazione può essere più lenta di uno stack ( O(log n)vs O(1)), gli heap consentono di liberare la memoria in una posizione arbitraria O(log n), rispetto a uno stackO(n)


4
A questo proposito, Facebook non rimuove il contenuto dal suo disco quando gli chiedi di rimuoverlo, rimuove semplicemente il puntatore ad esso. Evidentemente il sovraccarico di tentare di deframmentare o cercare di trovare lo spazio equivalente sul disco richiede troppo tempo alle velocità con cui scrivono i dati, quindi aggiungono semplicemente tutto sull'alto livello del disco.
Paul Tomblin,

Bene, i dischi di Facebook possono essere visti come un mucchio. E sono abbastanza sicuro che abbiano una specie di garbage collection per questi dischi.
deadalnix,

2
@deadalnix In realtà questo è un esempio dell'uso di uno stack enorme anziché dell'heap normalmente utilizzato per grandi quantità di memoria. Facebook è un caso speciale però. I dati vengono aggiunti molto più velocemente di quanto non vengano rimossi in modo che la deallocazione non faccia una differenza significativa nel tasso di crescita: è possibile includere deliberatamente perdite di memoria nella progettazione per ottenere tale allocazione O (1).
Tom Clarkson,

7
@PaulTomblin Il motivo principale per cui FB non rimuove il contenuto è che possono
estrarlo a

5
Facebook doesn't remove content from its disk when you ask it to remove it, it just removes the pointer to it- Che è essenzialmente ciò che accade quando si esegue una normale cancellazione di un file su qualsiasi sistema operativo.
Robert Harvey,

5

Lo stack è per thread, l'heap è a livello di processo

Se sono presenti 100 thread che elaborano tutti gli elementi di lavoro che inserisco in una coda, esattamente dove posso allocare gli elementi di lavoro in modo tale che uno qualsiasi dei 100 thread li possa vedere?

Esistono anche altri tipi di memoria

Ad esempio file mappati in memoria, memoria condivisa, I / O mappati (modalità kernel). L'argomento dell'efficienza è un po 'controverso in queste situazioni.


4

Uno stack è una struttura LIFO (last-in-first-out), in cima alla quale viene mantenuto un puntatore di riferimento (generalmente supportato dall'hardware). Detto questo, tutto ciò che si sta tentando di allocare nello stack anziché nell'heap dovrebbe essere una variabile locale in ogni funzione, nella parte superiore di questo stack. Quindi, la ragione principale contro uno stack è che la tua routine main () dovrebbe preallocare tutte le strutture di dati che il tuo programma utilizza (che dovrebbero essere in circolazione per l'intera durata del programma) in anticipo come tutte le strutture di dati allocate all'interno delle chiamate di funzione verranno infine rimosse quando tali chiamate di funzione ritornano e i loro frame o record di attivazione vengono espulsi dallo stack.


3
LIFO, non FIFO.
Pubblicazione del

a volte anche chiamato FILO;)
oenone

3

Gli stack funzionano alla grande per le allocazioni di memoria che obbediscono alle regole di LIFO (Last in First out), ovvero liberate la memoria nell'ordine inverso esatto in cui la allocate. LIFO è un modello di allocazione della memoria molto comune, forse il più comune. Ma non è l'unico modello, o anche l'unico modello comune. Per scrivere un programma efficiente in grado di affrontare un'ampia varietà di problemi, dobbiamo tenere conto dei modelli meno comuni, anche se ciò significa un'infrastruttura più complessa.

Se riesco a ottenere tutti i meta per un paragrafo: sei un principiante, come principiante apprezzi la semplicità e le regole in bianco e nero. Tuttavia, come principiante hai solo una visione spioncino della gamma di problemi e vincoli che devono essere sistemati dai programmi per computer. Stai entrando in una tecnologia che è stata in fase di sviluppo attivo per 75 anni. Non c'è niente di sbagliato nel chiedere perché le cose sono come sono, ma la risposta sarà generalmente "Sì, abbiamo provato il metodo semplice e diretto 50 anni fa, e si è scoperto che non funzionava molto bene per intere classi di problemi, quindi abbiamo dovuto fare qualcosa di più complicato ". Man mano che la tecnologia avanza, la semplicità deve generalmente lasciare il posto a efficienza e flessibilità.


0

Per un altro esempio, chiusure. Se impili pop, allora puoi perdere il record di attivazione della tua chiusura. Quindi, se vuoi che la funzione anonima e i suoi dati persistano, devi memorizzarla in un posto diverso dallo stack di runtime.


0

Tra le molte altre ragioni per l'allocazione dell'archiviazione heap.

Se si desidera restituire l'indirizzo di un oggetto a un programma chiamante, non si deve passare l'indirizzo di una variabile di stack, poiché la memoria dello stack verrà riutilizzata e possibilmente sovrascritta dalla successiva funzione chiamata. È necessario ottenere l'archiviazione richiesta utilizzando malloc () per assicurarsi che non venga sovrascritto dalle chiamate alle funzioni successive.

Puoi passare gli indirizzi degli oggetti dello stack dalla tua funzione alle funzioni che chiami, perché puoi garantire che esistano fino a quando il tuo programma "restituisce ()". Ma non appena la funzione ritorna, tutto lo stack di archiviazione è disponibile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.