Overflow dell'heap e overflow dello stack

4

Quindi come regola generale per evitare un overflow dello stack, gli oggetti grandi dovrebbero essere allocati all'heap (correggimi se ho torto). Ma, poiché l'heap e lo stack si espandono uno verso l'altro, questo non causerebbe l'overflow dell'heap o, in alternativa, limiterà lo spazio per lo stack e aumenteranno le possibilità di overflow dello stack?

    
posta Rayhunter 08.05.2013 - 03:32
fonte

6 risposte

6

Ovviamente, la memorizzazione di X byte di dati ridurrà la quantità di memoria libera di almeno X byte, indipendentemente da dove la metti.

big objects should be allocated to the heap

Non penso che questa sia la principale distinzione da disegnare.

Se hai bisogno che i dati siano accessibili al di fuori dello stack frame corrente (ad esempio come variabile globale o per passarlo a un altro thread), non puoi metterlo nello stack.

Lo stack si restringe quando viene ripristinata una subroutine, quindi tutti i dati memorizzati nel relativo frame dello stack vengono persi.

I dati sull'heap rimangono "vivi" finché non lo deselezioni.

Oltre a ciò, poiché la memoria dello stack è generalmente molto più limitata della memoria heap nella "vita reale" (non solo due sezioni illimitate che crescono l'una verso l'altra come hai fatto nella tua domanda), potresti voler mettere qualcosa di grande sul heap, anche se potresti metterlo in pila. Ad esempio, Java posiziona tutto nell'heap. Lo svantaggio è una gestione della memoria più complessa.

    
risposta data 08.05.2013 - 03:35
fonte
4

Considerate le ipotesi sottostanti, sì - non importa se lo si accumula sul pavimento o se lo si appende al soffitto.

La tua domanda indica che hai scelto solo l'heap o lo stack. (E non stiamo parlando di oggetti molto grandi che potrebbero essere migliori di altri e meno comuni scenari).

Assunzione

  1. Lo stack e l'heap si trovano nella stessa area di memoria di processo fissa e non esistono altre limitazioni.

  2. Il tuo nuovo oggetto grande verrà aggiunto alla fine dello stack rispettivo dell'heap.

  3. Gli oggetti nello stack vengono deallocati al ritorno della funzione. Per rendere questa scelta davvero simmetrica, assumiamo che ciò sia vero anche quando si utilizza l'heap.

Ora cosa succede se rimuoviamo queste ipotesi?

  1. Lo stack può essere limitato, ad esempio sul vecchio sistema operativo o nelle applicazioni multi-threading ogni thread può ottenere il proprio stack con dimensioni limitate, quindi l'allocazione di grandi oggetti nello stack potrebbe sovraccaricarla. L'heap, d'altra parte, è generalmente supportato dalla gestione della memoria virtuale che consente di aumentare durante la vita del processo.

  2. Normalmente il tuo heap conterrà dei buchi: lo spazio residuo degli oggetti che vengono deallocati, se sei fortunato il tuo nuovo oggetto si adatta appena all'interno di tale buco. Un buco del genere potrebbe, ad esempio, aprirsi quando due o più oggetti più piccoli, assegnati ad indirizzi adiacenti, sono stati deallocati. Spesso le allocazioni usando l'heap possono essere soddisfatte senza far crescere l'heap. D'altra parte l'utilizzo dell'heap per piccoli oggetti potrebbe invitare la frammentazione della memoria (vedi anche Mozilla), questo dipende molto dall'allocatore.

    Al contrario, la creazione di una variabile sullo stack aumenta lo stack dello stesso importo.

  3. Supponiamo che tu voglia restituire il tuo oggetto alla funzione chiamante. Quindi i grandi oggetti nello stack devono essere copiati come interi! È più efficiente sia in termini di tempo che di spazio restituire un puntatore all'indirizzo di memoria dell'heap, che si adatta facilmente all'interno di un registro. Gli oggetti piccoli (come numeri interi, float, doppi) spesso si inseriscono in un registro (in genere 4 o 8 byte) o due. Fortunatamente il moderno compilatore ottimizza questo caso, quindi non dobbiamo preoccuparci di questo così tanto.

Riprendi : il comportamento esatto dipende molto dal tuo sistema operativo e dal tuo compilatore, su Linux solo il cielo è il limite.

Addendum domanda SO correlata

    
risposta data 08.05.2013 - 16:37
fonte
2

Stack overflow è ciò che accade quando un'architettura con uno stack limitato tenta di incrementare il puntatore dello stack oltre il suo valore massimo possibile. A volte questo è un limite rigido, come nel 6502, che aveva uno stack da 256 byte in una posizione fissa nella memoria e un puntatore allo stack di un byte. Spingendo un 257 ° byte nello stack si otterrebbe il puntatore dello stack per avvolgere e coprire il fondo dello stack. Nei sistemi moderni, di solito è un limite soft che può essere ripristinato prima di avviare il programma o mentre il programma è in esecuzione. Passa sopra quella linea con un sistema POSIX-y e ottieni un errore di segmentazione. È anche teoricamente possibile avere uno stack che continua a crescere verso il basso fino a quando la memoria è disponibile per tenerlo.

Il fenomeno a cui ti riferisci è chiamato collisione stack-heap , dove si cresce così tanto che si sovrappone all'altro. Li hai visti molto più spesso quando gli spazi degli indirizzi (e la memoria) erano più piccoli; ora è molto meno comune perché non esauriamo quasi altrettanto spesso.

Il mio consiglio è di basare le tue decisioni su come smaltire la memoria. Se è qualcosa che può scomparire quando si esce dal campo di applicazione, metterlo in pila, perché ti farà risparmiare il mal di testa di gestirlo. Se ha bisogno di stare intorno alla vita dell'attuale scope, mettilo sullo heap. Non prendere questo come una regola hard-and-fast, perché ci sono situazioni in cui non si applica. Usa il tuo giudizio: se stai per allocare qualcosa che sai ti farà uscire di pila, usa l'heap e assicurati di ripulirti dopo di te. Le allocazioni di grandi dimensioni tendono a essere riutilizzate, motivo per cui vengono messe sullo heap più spesso.

La linea di fondo è che se la memoria è così breve che lo stack e l'heap si scontrano, si hanno problemi più grandi di dove mettere ciò che si assegna.

    
risposta data 08.05.2013 - 15:22
fonte
1

La risposta è chiaramente dipendente dalla piattaforma, dipendente dal compilatore e dipendente dalla libreria.

Ma considera che in un processo ci sia uno stack per thread e un numero di heap che il processo richiede ai sistemi (normalmente 1, ma ce ne possono essere di più: un SO può avere API per allocare vari "heap").

Ci sono sistemi operativi che danno a un processo un determinato blocco, in cui l'heap e gli stack sono presi per sottrazione, e i sistemi operativi che danno blocchi distinti al processo, ciascuno dei quali traccia un diverso blocco di memoria fisica.

Su sistemi operativi e processori che implementano la memoria virtuale, lo spazio degli indirizzi del processo viene mappato alla memoria fisica da alcune tabelle puntatore invisibili al processo stesso.

Quando diciamo che lo stack cresce e si restringe stiamo parlando nella "visualizzazione del processo". Nella "vista OS", una pila è solo un blocco che non cresce o si riduce a ogni chiamata / ritorno. è solo più o meno pieno. Quando diventa pieno, deve essere riallocato su un'altra area di memoria fisica con più spazio. Il costo di "riallocazione di uno stack frame" è pesante (non deve avere "buchi" all'interno), quindi molti sistemi operativi non consentono a uno stack di crescere su una dimensione predefinita. Ma l'heap dos non ha bisogno di tale cura: se un blocco heap è pieno, viene aggiunto un altro blocco. Non c'è bisogno di renderlo contiguo. Questa -una piattaforma multipla- dà la percezione (nello "spazio del processo") che una pila è limitata (di solito 4MB o simile) mentre l'heap è "infinito" (e limitato solo dalla memoria fisica o dall'area di swap).

    
risposta data 12.05.2013 - 11:53
fonte
0

Il punto strong dello stack è l'allocazione ultraveloce e la deallocazione della memoria che segue un modello LIFO (l'ultimo allocato viene prima deallocato). Se hai bisogno di memoria che seguirà un diverso schema di allocazione / deallocazione, dovrai ottenerlo dall'heap.

Si noti che per i sistemi operativi multiprocessing comuni ogni processo ottiene il proprio stack e il proprio heap. In genere lo stack per un processo è solo da 1 MB a 5 MB. La piccola dimensione è il motivo per cui dovresti evitare di allocare grandi strutture nello stack. Al contrario, l'heap può crescere per consumare la maggior parte della memoria fisica consentita dal sistema operativo. Sì, puoi finalmente utilizzare tutto l'heap che il sistema operativo ti garantisce, ma di solito sarà un fattore 1000 volte più grande della pila.

    
risposta data 08.05.2013 - 20:38
fonte
0

Questo è qualcosa di cui non devi preoccuparti. L'heap e lo stack vengono gestiti separatamente. Più in particolare, l'heap è supportato dalla memoria virtuale, quindi se lo elabora globalmente il sistema operativo farà una pagina. Lo stack è una cosa separata che normalmente non si espande oltre una certa dimensione. Non si assegna liberamente dall'heap.

La ragione per cui dicono che allocare le grandi cose dall'heap è che l'heap può gestirlo e lo stack, una dimensione fissa, non può.

Se osservi più da vicino la pila, potresti pensare che possa continuare a crescere per sempre, o fino a esaurimento della memoria, e hai ragione. Tuttavia, la maggior parte / tutti i sistemi operativi monitorano le dimensioni dello stack e impediscono che continui a crescere.

Infine, quando si assegnano i dati dall'heap, si tratta di dati gestiti. Ciò che intendo è che il sistema operativo sta allocando / deallocando i dati quando ne hai bisogno e quando è fuori. Se l'heap dovesse esaurirsi, malloc restituirebbe un valore NULL. Si noti che sulla maggior parte dei sistemi operativi la memoria virtuale esegue il back up in modo che malloc restituisca sempre qualcosa.

    
risposta data 08.05.2013 - 21:02
fonte

Leggi altre domande sui tag