Domanda

I libri sul linguaggio di programmazione spiegano che i tipi di valore vengono creati su pila, e i tipi di riferimento vengono creati in mucchio, senza spiegare cosa siano queste due cose.Non ho letto una spiegazione chiara di questo.Capisco cosa una pila È.Ma,

  • Dove e cosa sono (fisicamente nella memoria di un computer reale)?
  • In che misura sono controllati dal sistema operativo o dal runtime del linguaggio?
  • Qual è il loro ambito?
  • Cosa determina la dimensione di ciascuno di essi?
  • Cosa rende uno più veloce?
È stato utile?

Soluzione

Lo stack è la memoria riservata come spazio di lavoro per un thread di esecuzione.Quando viene chiamata una funzione, un blocco viene riservato in cima allo stack per le variabili locali e alcuni dati contabili.Quando la funzione ritorna, il blocco diventa inutilizzato e può essere utilizzato la prossima volta che viene chiamata una funzione.Lo stack è sempre riservato in ordine LIFO (last in first out);il blocco prenotato più recentemente è sempre il blocco successivo da liberare.Ciò rende davvero semplice tenere traccia dello stack;liberare un blocco dallo stack non è altro che regolare un puntatore.

L'heap è la memoria riservata all'allocazione dinamica.A differenza dello stack, non esiste uno schema imposto per l'allocazione e la deallocazione dei blocchi dall'heap;puoi allocare un blocco in qualsiasi momento e liberarlo in qualsiasi momento.Ciò rende molto più complesso tenere traccia di quali parti dell'heap sono allocate o libere in un dato momento;sono disponibili molti allocatori di heap personalizzati per ottimizzare le prestazioni dell'heap per diversi modelli di utilizzo.

Ogni thread riceve uno stack, mentre in genere esiste un solo heap per l'applicazione (sebbene non sia raro avere più heap per diversi tipi di allocazione).

Per rispondere direttamente alle tue domande:

In che misura sono controllati dal sistema operativo o dal runtime del linguaggio?

Il sistema operativo alloca lo stack per ogni thread a livello di sistema quando viene creato il thread.In genere il sistema operativo viene chiamato dal Language Runtime per allocare l'heap per l'applicazione.

Qual è il loro ambito?

Lo stack è collegato a un thread, quindi quando il thread esce lo stack viene recuperato.L'heap viene in genere allocato all'avvio dell'applicazione dal runtime e viene recuperato quando l'applicazione (processo tecnico) termina.

Cosa determina la dimensione di ciascuno di essi?

La dimensione dello stack viene impostata quando viene creato un thread.La dimensione dell'heap viene impostata all'avvio dell'applicazione, ma può aumentare in base alle esigenze di spazio (l'allocatore richiede più memoria dal sistema operativo).

Cosa rende uno più veloce?

Lo stack è più veloce perché il modello di accesso rende banale allocare e deallocare la memoria da esso (un puntatore/intero viene semplicemente incrementato o decrementato), mentre l'heap ha una contabilità molto più complessa coinvolta in un'allocazione o deallocazione.Inoltre, ogni byte nello stack tende ad essere riutilizzato molto frequentemente, il che significa che tende ad essere mappato nella cache del processore, rendendolo molto veloce.Un altro problema di prestazioni per l'heap è che l'heap, essendo principalmente una risorsa globale, in genere deve essere sicuro multi-threading, ad es.ogni allocazione e deallocazione deve essere, in genere, sincronizzata con "tutti" gli altri accessi all'heap nel programma.

Una chiara dimostrazione:
Fonte immagine: vikashazrati.wordpress.com

Altri suggerimenti

Pila:

  • Memorizzato nella RAM del computer proprio come l'heap.
  • Le variabili create nello stack usciranno dall'ambito e verranno deallocate automaticamente.
  • Molto più veloce da allocare rispetto alle variabili nell'heap.
  • Implementato con una struttura dati dello stack reale.
  • Memorizza dati locali, indirizzi di ritorno, utilizzati per il passaggio dei parametri.
  • Può verificarsi un overflow dello stack quando viene utilizzata una parte eccessiva dello stack (principalmente a causa di ricorsione infinita o troppo profonda, allocazioni molto grandi).
  • I dati creati nello stack possono essere utilizzati senza puntatori.
  • Utilizzeresti lo stack se sai esattamente quanti dati devi allocare prima del momento della compilazione e non è troppo grande.
  • Di solito ha una dimensione massima già determinata all'avvio del programma.

Mucchio:

  • Memorizzato nella RAM del computer proprio come lo stack.
  • In C++, le variabili nell'heap devono essere distrutte manualmente e non escono mai dall'ambito.I dati vengono liberati con delete, delete[], O free.
  • Più lento da allocare rispetto alle variabili sullo stack.
  • Utilizzato su richiesta per allocare un blocco di dati da utilizzare da parte del programma.
  • Può verificarsi una frammentazione quando sono presenti numerose allocazioni e deallocazioni.
  • In C++ o C, i dati creati sull'heap verranno puntati da puntatori e allocati con new O malloc rispettivamente.
  • Possono verificarsi errori di allocazione se viene richiesta l'allocazione di un buffer troppo grande.
  • Utilizzeresti l'heap se non sai esattamente quanti dati ti serviranno in fase di esecuzione o se devi allocare molti dati.
  • Responsabile delle perdite di memoria.

Esempio:

int foo()
{
  char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
  bool b = true; // Allocated on the stack.
  if(b)
  {
    //Create 500 bytes on the stack
    char buffer[500];

    //Create 500 bytes on the heap
    pBuffer = new char[500];

   }//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;

Il punto più importante è che heap e stack sono termini generici per indicare le modalità di allocazione della memoria.Possono essere implementati in molti modi diversi e i termini si applicano ai concetti di base.

  • In una pila di oggetti, gli oggetti si trovano uno sopra l'altro nell'ordine in cui sono stati posizionati lì e puoi rimuovere solo quello in alto (senza rovesciare il tutto).

    Stack like a stack of papers

    La semplicità di uno stack è che non è necessario mantenere una tabella contenente un record di ciascuna sezione di memoria allocata;l'unica informazione di stato necessaria è un singolo puntatore alla fine dello stack.Per allocare e deallocare, basta incrementare e decrementare quel singolo puntatore.Nota:a volte è possibile implementare uno stack in modo che inizi nella parte superiore di una sezione di memoria e si estenda verso il basso anziché crescere verso l'alto.

  • In un heap non esiste un ordine particolare nel modo in cui vengono posizionati gli elementi.Puoi raggiungere e rimuovere gli elementi in qualsiasi ordine perché non esiste un elemento "in alto" chiaro.

    Heap like a heap of licorice allsorts

    L'allocazione dell'heap richiede il mantenimento di un record completo di quale memoria è allocata e quale no, nonché una certa manutenzione generale per ridurre la frammentazione, trovare segmenti di memoria contigui sufficientemente grandi da adattarsi alla dimensione richiesta e così via.La memoria può essere deallocata in qualsiasi momento lasciando spazio libero.A volte un allocatore di memoria eseguirà attività di manutenzione come la deframmentazione della memoria spostando la memoria allocata o la raccolta dei rifiuti, identificando in fase di esecuzione quando la memoria non è più nell'ambito e deallocandola.

Queste immagini dovrebbero descrivere abbastanza bene i due modi di allocare e liberare memoria in uno stack e in un heap.Yum!

  • In che misura sono controllati dal sistema operativo o dal runtime del linguaggio?

    Come accennato, heap e stack sono termini generali e possono essere implementati in molti modi.I programmi per computer hanno in genere uno stack chiamato a stack di chiamate che memorizza informazioni rilevanti per la funzione corrente come un puntatore a qualunque funzione da cui è stata chiamata e qualsiasi variabile locale.Poiché le funzioni chiamano altre funzioni e poi restituiscono, lo stack aumenta e si riduce per contenere informazioni dalle funzioni più in basso nello stack di chiamate.Un programma non ha realmente il controllo in fase di esecuzione su di esso;è determinato dal linguaggio di programmazione, dal sistema operativo e persino dall'architettura del sistema.

    Un heap è un termine generale utilizzato per qualsiasi memoria allocata in modo dinamico e casuale;cioè.fuori servizio.La memoria viene in genere allocata dal sistema operativo, con l'applicazione che chiama le funzioni API per eseguire questa allocazione.È necessario un bel po' di sovraccarico nella gestione della memoria allocata dinamicamente, che di solito viene gestita dal sistema operativo.

  • Qual è il loro ambito?

    Lo stack di chiamate è un concetto di livello così basso che non si riferisce all'ambito nel senso di programmazione.Se disassembli del codice vedrai relativi riferimenti di stile del puntatore a porzioni dello stack, ma per quanto riguarda un linguaggio di livello superiore, il linguaggio impone le proprie regole di ambito.Un aspetto importante di uno stack, tuttavia, è che una volta che una funzione ritorna, tutto ciò che è locale a quella funzione viene immediatamente liberato dallo stack.Funziona nel modo in cui ti aspetteresti che funzioni, dato il funzionamento dei tuoi linguaggi di programmazione.Nel mucchio, è anche difficile da definire.L'ambito è tutto ciò che viene esposto dal sistema operativo, ma il tuo linguaggio di programmazione probabilmente aggiunge le sue regole su cosa sia un "ambito" nella tua applicazione.L'architettura del processore e il sistema operativo utilizzano l'indirizzamento virtuale, che il processore traduce in indirizzi fisici e si verificano errori di pagina, ecc.Tengono traccia di quali pagine appartengono a quali applicazioni.Non devi mai preoccuparti di questo, però, perché usi semplicemente il metodo utilizzato dal tuo linguaggio di programmazione per allocare e liberare memoria e verificare la presenza di errori (se l'allocazione/liberazione fallisce per qualsiasi motivo).

  • Cosa determina la dimensione di ciascuno di essi?

    Ancora una volta, dipende dal linguaggio, dal compilatore, dal sistema operativo e dall'architettura.Uno stack è solitamente pre-allocato, perché per definizione deve essere memoria contigua (ne parleremo più avanti nell'ultimo paragrafo).Il compilatore del linguaggio o il sistema operativo ne determinano le dimensioni.Non memorizzi enormi quantità di dati nello stack, quindi sarà abbastanza grande da non essere mai utilizzato completamente, tranne nei casi di ricorsione infinita indesiderata (da qui "stack overflow") o altre decisioni di programmazione insolite.

    Un heap è un termine generale per tutto ciò che può essere allocato dinamicamente.A seconda del modo in cui lo guardi, cambia costantemente dimensione.Nei moderni processori e sistemi operativi il modo esatto in cui funziona è comunque molto astratto, quindi normalmente non devi preoccuparti molto di come funziona nel profondo, tranne che (nelle lingue in cui te lo consente) non devi usare la memoria che non hai ancora allocato o memoria che hai liberato.

  • Cosa rende uno più veloce?

    Lo stack è più veloce perché tutta la memoria libera è sempre contigua.Non è necessario mantenere un elenco di tutti i segmenti di memoria libera, solo un singolo puntatore all'attuale cima dello stack.I compilatori solitamente memorizzano questo puntatore in un modo speciale e veloce Registrati per questo scopo.Inoltre, le operazioni successive su uno stack sono solitamente concentrate in aree di memoria molto vicine, il che a un livello molto basso è utile per l'ottimizzazione da parte delle cache on-die del processore.

(Ho spostato questa risposta da un'altra domanda che era più o meno una copia di questa.)

La risposta alla tua domanda è specifica dell'implementazione e può variare a seconda dei compilatori e delle architetture del processore.Tuttavia, ecco una spiegazione semplificata.

  • Sia lo stack che l'heap sono aree di memoria allocate dal sistema operativo sottostante (spesso memoria virtuale mappata su richiesta alla memoria fisica).
  • In un ambiente multi-thread ogni thread avrà il proprio stack completamente indipendente ma condividerà l'heap.L'accesso simultaneo deve essere controllato nell'heap e non è possibile nello stack.

Il mucchio

  • L'heap contiene un elenco collegato di blocchi utilizzati e liberi.Nuove allocazioni nell'heap (by new O malloc) vengono soddisfatti creando un blocco adatto da uno dei blocchi liberi.Ciò richiede l'aggiornamento dell'elenco dei blocchi nell'heap.Questo metainformazioni Anche i blocchi nell'heap vengono memorizzati nell'heap, spesso in una piccola area proprio davanti a ogni blocco.
  • Man mano che l'heap cresce, nuovi blocchi vengono spesso allocati dagli indirizzi più bassi verso quelli più alti.Quindi puoi pensare all'heap come a mucchio di blocchi di memoria che aumentano di dimensioni man mano che la memoria viene allocata.Se l'heap è troppo piccolo per un'allocazione, la dimensione può spesso essere aumentata acquisendo più memoria dal sistema operativo sottostante.
  • L'allocazione e la deallocazione di molti piccoli blocchi può lasciare l'heap in uno stato in cui sono presenti molti piccoli blocchi liberi intervallati tra i blocchi utilizzati.Una richiesta di allocazione di un blocco di grandi dimensioni potrebbe non riuscire perché nessuno dei blocchi liberi è abbastanza grande da soddisfare la richiesta di allocazione anche se la dimensione combinata dei blocchi liberi potrebbe essere sufficientemente grande.Questo è chiamato frammentazione dell'heap.
  • Quando un blocco utilizzato adiacente a un blocco libero viene deallocato, il nuovo blocco libero può essere unito al blocco libero adiacente per creare un blocco libero più grande riducendo efficacemente la frammentazione dell'heap.

The heap

La pila

  • Lo stack spesso funziona in stretto contatto con un registro speciale sulla CPU denominato the puntatore dello stack.Inizialmente il puntatore dello stack punta alla cima dello stack (l'indirizzo più alto sullo stack).
  • La CPU dispone di istruzioni speciali per spingendo valori sullo stack e scoppiando toglierli dalla pila.Ogni spingere memorizza il valore nella posizione corrente del puntatore dello stack e diminuisce il puntatore dello stack.UN pop recupera il valore puntato dallo stack pointer e quindi aumenta lo stack pointer (non lasciarti confondere dal fatto che aggiungendo un valore per lo stack diminuisce il puntatore dello stack e rimuovendo un valore aumenta Esso.Ricorda che la pila cresce fino in fondo).I valori memorizzati e recuperati sono i valori dei registri della CPU.
  • Quando viene chiamata una funzione, la CPU utilizza istruzioni speciali che spingono la corrente puntatore di istruzione, cioè.l'indirizzo del codice in esecuzione sullo stack.La CPU passa quindi alla funzione impostando il puntatore di istruzioni sull'indirizzo della funzione chiamato.Successivamente, quando la funzione ritorna, il vecchio puntatore all'istruzione viene estratto dallo stack e l'esecuzione riprende dal codice subito dopo la chiamata alla funzione.
  • Quando viene inserita una funzione, il puntatore dello stack viene ridotto per allocare più spazio nello stack per le variabili locali (automatiche).Se la funzione ha una variabile locale a 32 bit, quattro byte vengono messi da parte nello stack.Quando la funzione ritorna, il puntatore dello stack viene spostato indietro per liberare l'area allocata.
  • Se una funzione ha parametri, questi vengono inseriti nello stack prima della chiamata alla funzione.Il codice nella funzione è quindi in grado di risalire lo stack dal puntatore dello stack corrente per individuare questi valori.
  • Le chiamate alle funzioni di annidamento funzionano a meraviglia.Ogni nuova chiamata allocherà i parametri della funzione, l'indirizzo di ritorno e lo spazio per le variabili locali e queste record di attivazione può essere impilato per chiamate nidificate e si svolgerà nel modo corretto quando le funzioni ritornano.
  • Poiché lo stack è un blocco limitato di memoria, puoi causare a overflow dello stack chiamando troppe funzioni annidate e/o allocando troppo spazio per le variabili locali.Spesso l'area di memoria utilizzata per lo stack è impostata in modo tale che la scrittura sotto il fondo (l'indirizzo più basso) dello stack attiverà una trap o un'eccezione nella CPU.Questa condizione eccezionale può quindi essere rilevata dal runtime e convertita in una sorta di eccezione di overflow dello stack.

The stack

È possibile allocare una funzione sull'heap anziché su uno stack?

No, i record di attivazione delle funzioni (ad es.variabili locali o automatiche) vengono allocate nello stack che viene utilizzato non solo per memorizzare queste variabili, ma anche per tenere traccia delle chiamate di funzioni annidate.

Il modo in cui viene gestito l'heap dipende davvero dall'ambiente di runtime.C utilizza malloc e C++ utilizza new, ma molte altre lingue hanno la garbage collection.

Tuttavia, lo stack è una funzionalità di basso livello strettamente legata all'architettura del processore.Aumentare l'heap quando non c'è abbastanza spazio non è troppo difficile poiché può essere implementato nella chiamata alla libreria che gestisce l'heap.Tuttavia, l'aumento dello stack è spesso impossibile poiché l'overflow dello stack viene scoperto solo quando è troppo tardi;e chiudere il thread di esecuzione è l'unica opzione praticabile.

Nel seguente codice C#

public void Method1()
{
    int i = 4;
    int y = 2;
    class1 cls1 = new class1();
}

Ecco come viene gestita la memoria

Picture of variables on the stack

Local Variables che devono durare solo finché l'invocazione della funzione va nello stack.L'heap viene utilizzato per variabili di cui non conosciamo la durata in anticipo ma ci aspettiamo che durino un po'.Nella maggior parte dei linguaggi è fondamentale sapere in fase di compilazione quanto è grande una variabile se vogliamo memorizzarla nello stack.

Gli oggetti (che variano in dimensioni man mano che li aggiorniamo) vanno nell'heap perché non sappiamo al momento della creazione quanto dureranno.In molti linguaggi l'heap viene sottoposto a garbage collection per trovare oggetti (come l'oggetto cls1) che non hanno più alcun riferimento.

In Java, la maggior parte degli oggetti finisce direttamente nell'heap.In linguaggi come C/C++, le strutture e le classi possono spesso rimanere nello stack quando non hai a che fare con i puntatori.

Ulteriori informazioni possono essere trovate qui:

La differenza tra allocazione della memoria stack e heap « timmurphy.org

e qui:

Creazione di oggetti nello stack e nell'heap

Questo articolo è la fonte dell'immagine sopra: Sei importanti concetti .NET:Stack, heap, tipi di valore, tipi di riferimento, boxing e unboxing: CodeProject

tieni però presente che potrebbe contenere alcune imprecisioni.

La pilaQuando chiami una funzione, gli argomenti di quella funzione più qualche altro sovraccarico vengono messi nello stack.Qui vengono memorizzate anche alcune informazioni (ad esempio dove andare al ritorno).Quando dichiari una variabile all'interno della tua funzione, anche quella variabile viene allocata nello stack.

La deallocazione dello stack è piuttosto semplice perché la deallocazione avviene sempre nell'ordine inverso rispetto a quello in cui lo fai.Gli elementi dello stack vengono aggiunti quando si immettono funzioni, i dati corrispondenti vengono rimossi quando si esce da esse.Ciò significa che tendi a rimanere in una piccola regione dello stack a meno che non chiami molte funzioni che chiamano molte altre funzioni (o crei una soluzione ricorsiva).

Il mucchioL'heap è un nome generico per indicare la posizione in cui inserisci i dati che crei al volo.Se non sai quante astronavi creerà il tuo programma, probabilmente utilizzerai l'operatore new (o malloc o equivalente) per creare ciascuna astronave.Questa allocazione durerà per un po', quindi è probabile che libereremo le cose in un ordine diverso da quello in cui le abbiamo create.

Pertanto, l'heap è molto più complesso, perché finiscono per esserci regioni di memoria inutilizzate interlacciate con blocchi che lo sono: la memoria viene frammentata.Trovare la memoria libera della dimensione necessaria è un problema difficile.Questo è il motivo per cui l'heap dovrebbe essere evitato (sebbene venga ancora utilizzato spesso).

ImplementazioneL'implementazione sia dello stack che dell'heap dipende solitamente dal runtime/sistema operativo.Spesso i giochi e altre applicazioni critiche in termini di prestazioni creano le proprie soluzioni di memoria che prendono una grande porzione di memoria dall'heap e poi la distribuiscono internamente per evitare di fare affidamento sul sistema operativo per la memoria.

Questo è pratico solo se l'utilizzo della memoria è molto diverso dalla norma, ad esempio per i giochi in cui carichi un livello in un'operazione enorme e puoi buttare via tutto in un'altra operazione enorme.

Posizione fisica nella memoriaQuesto è meno rilevante di quanto pensi a causa di una tecnologia chiamata Memoria virtuale il che fa credere al tuo programma che tu abbia accesso a un certo indirizzo dove i dati fisici sono da qualche altra parte (anche sul disco rigido!).Gli indirizzi che ottieni per lo stack sono in ordine crescente man mano che l'albero delle chiamate diventa più profondo.Gli indirizzi per l'heap sono imprevedibili (cioè specifici dell'implementazione) e francamente non importanti.

Chiarire, questa risposta contiene informazioni errate (Tommaso corretto la sua risposta dopo i commenti, fantastico :)).Altre risposte evitano semplicemente di spiegare cosa significa l'allocazione statica.Di seguito spiegherò quindi le tre principali forme di allocazione e il modo in cui solitamente si riferiscono all'heap, allo stack e al segmento di dati.Mostrerò anche alcuni esempi sia in C/C++ che in Python per aiutare le persone a capire.

Le variabili "statiche" (AKA allocate staticamente) non vengono allocate nello stack.Non dare per scontato che sia così: molte persone lo fanno solo perché "statico" assomiglia molto a "stack".In realtà non esistono né nello stack né nell'heap.Fanno parte di quello che viene chiamato segmento di dati.

Tuttavia, in genere è meglio considerare "scopo" E "tutta la vita" piuttosto che "stack" e "heap".

L'ambito si riferisce a quali parti del codice possono accedere a una variabile.Generalmente pensiamo a ambito locale (è possibile accedervi solo dalla funzione corrente) versus portata globale (è possibile accedervi ovunque) sebbene l'ambito possa diventare molto più complesso.

La durata si riferisce al momento in cui una variabile viene allocata e deallocata durante l'esecuzione del programma.Di solito ci pensiamo allocazione statica (la variabile persisterà per tutta la durata del programma, rendendola utile per memorizzare le stesse informazioni attraverso diverse chiamate di funzione) versus assegnazione automatica (la variabile persiste solo durante una singola chiamata a una funzione, rendendola utile per memorizzare informazioni che vengono utilizzate solo durante la funzione e che possono essere scartate una volta terminata) rispetto a allocazione dinamica (variabili la cui durata è definita in fase di esecuzione, anziché in fase di compilazione come statica o automatica).

Sebbene la maggior parte dei compilatori e degli interpreti implementino questo comportamento in modo simile in termini di utilizzo di stack, heap, ecc., un compilatore a volte può infrangere queste convenzioni se lo desidera purché il comportamento sia corretto.Ad esempio, a causa dell'ottimizzazione, una variabile locale può esistere solo in un registro o essere rimossa completamente, anche se la maggior parte delle variabili locali esiste nello stack.Come è stato sottolineato in alcuni commenti, sei libero di implementare un compilatore che non utilizzi nemmeno uno stack o un heap, ma invece alcuni altri meccanismi di archiviazione (raramente fatti, poiché stack e heap sono ottimi per questo).

Fornirò del semplice codice C annotato per illustrare tutto questo.Il modo migliore per imparare è eseguire un programma in un debugger e osservarne il comportamento.Se preferisci leggere Python, vai alla fine della risposta :)

// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;

// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;

// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {

    // Statically allocated in the data segment when the program is first loaded
    // Deallocated when the program/DLL exits
    // scope - can be accessed only within MyFunction()
    static int someLocalStaticVariable;

    // Allocated on the stack each time MyFunction is called
    // Deallocated when MyFunction returns
    // scope - can be accessed only within MyFunction()
    int someLocalVariable;

    // A *pointer* is allocated on the stack each time MyFunction is called
    // This pointer is deallocated when MyFunction returns
    // scope - the pointer can be accessed only within MyFunction()
    int* someDynamicVariable;

    // This line causes space for an integer to be allocated in the heap
    // when this line is executed. Note this is not at the beginning of
    // the call to MyFunction(), like the automatic variables
    // scope - only code within MyFunction() can access this space
    // *through this particular variable*.
    // However, if you pass the address somewhere else, that code
    // can access it too
    someDynamicVariable = new int;


    // This line deallocates the space for the integer in the heap.
    // If we did not write it, the memory would be "leaked".
    // Note a fundamental difference between the stack and heap
    // the heap must be managed. The stack is managed for us.
    delete someDynamicVariable;

    // In other cases, instead of deallocating this heap space you
    // might store the address somewhere more permanent to use later.
    // Some languages even take care of deallocation for you... but
    // always it needs to be taken care of at runtime by some mechanism.

    // When the function returns, someArgument, someLocalVariable
    // and the pointer someDynamicVariable are deallocated.
    // The space pointed to by someDynamicVariable was already
    // deallocated prior to returning.
    return;
}

// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.

Un esempio particolarmente significativo del motivo per cui è importante distinguere tra durata e ambito è che una variabile può avere ambito locale ma durata statica, ad esempio "someLocalStaticVariable" nell'esempio di codice precedente.Tali variabili possono rendere molto confuse le nostre abitudini di denominazione comuni ma informali.Ad esempio quando diciamo "Locale"di solito intendiamo"variabile allocata automaticamente con ambito locale" e quando diciamo globale di solito intendiamo "variabile allocata staticamente con ambito globale".Sfortunatamente quando si tratta di cose come "variabili allocate staticamente con ambito file"molte persone dicono semplicemente..."eh???".

Alcune delle scelte di sintassi in C/C++ aggravano questo problema: ad esempio molte persone pensano che le variabili globali non siano "statiche" a causa della sintassi mostrata di seguito.

int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation

int main() {return 0;}

Tieni presente che l'inserimento della parola chiave "static" nella dichiarazione precedente impedisce a var2 di avere un ambito globale.Tuttavia, la var1 globale ha un'allocazione statica.Questo non è intuitivo!Per questo motivo, cerco di non usare mai la parola "statico" quando descrivo l'ambito, e di dire invece qualcosa come "file" o "file limitato".Tuttavia molte persone usano la frase "statico" o "ambito statico" per descrivere una variabile a cui è possibile accedere solo da un file di codice.Nel contesto della vita, "statico" Sempre significa che la variabile viene allocata all'avvio del programma e deallocata all'uscita del programma.

Alcune persone pensano a questi concetti come specifici di C/C++.Non sono.Ad esempio, l'esempio Python riportato di seguito illustra tutti e tre i tipi di allocazione (sono possibili alcune sottili differenze nei linguaggi interpretati di cui non parlerò qui).

from datetime import datetime

class Animal:
    _FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated

    def PetAnimal(self):
        curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
        print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)

class Cat(Animal):
    _FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's

class Dog(Animal):
    _FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!


if __name__ == "__main__":
    whiskers = Cat() # Dynamically allocated
    fido = Dog() # Dynamically allocated
    rinTinTin = Dog() # Dynamically allocated

    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

    Dog._FavoriteFood = 'milkbones'
    whiskers.PetAnimal()
    fido.PetAnimal()
    rinTinTin.PetAnimal()

# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones

Altri hanno risposto abbastanza bene alle linee generali, quindi aggiungerò alcuni dettagli.

  1. Stack e heap non devono essere necessariamente singolari.Una situazione comune in cui hai più di uno stack è se hai più di un thread in un processo.In questo caso ogni thread ha il proprio stack.Puoi anche avere più di un heap, ad esempio alcune configurazioni DLL possono comportare l'allocazione di DLL diverse da heap diversi, motivo per cui generalmente è una cattiva idea rilasciare memoria allocata da una libreria diversa.

  2. In C puoi ottenere il vantaggio dell'allocazione di lunghezza variabile attraverso l'uso di alloca, che alloca sullo stack, al contrario di alloc, che alloca sull'heap.Questa memoria non sopravviverà all'istruzione return, ma è utile per un buffer di memoria.

  3. Creare un enorme buffer temporaneo su Windows che non usi molto non è gratuito.Questo perché il compilatore genererà un ciclo di sonda dello stack che viene chiamato ogni volta che viene inserita la funzione per assicurarsi che lo stack esista (perché Windows utilizza una singola pagina di guardia alla fine dello stack per rilevare quando è necessario aumentare lo stack.Se accedi alla memoria a più di una pagina dalla fine dello stack, andrai in crash).Esempio:

void myfunction()
{
   char big[10000000];
   // Do something that only uses for first 1K of big 99% of the time.
}

Altri hanno risposto direttamente alla tua domanda, ma quando si cerca di comprendere lo stack e l'heap, penso che sia utile considerare il layout della memoria di un processo UNIX tradizionale (senza thread e mmap()allocatori).IL Glossario della gestione della memoria la pagina web ha un diagramma di questo layout di memoria.

Lo stack e l'heap si trovano tradizionalmente alle estremità opposte dello spazio degli indirizzi virtuali del processo.Lo stack cresce automaticamente quando si accede, fino a una dimensione impostata dal kernel (che può essere regolata con setrlimit(RLIMIT_STACK, ...)).L'heap cresce quando l'allocatore di memoria richiama il file brk() O sbrk() chiamata di sistema, mappando più pagine di memoria fisica nello spazio degli indirizzi virtuali del processo.

Nei sistemi senza memoria virtuale, come alcuni sistemi embedded, spesso si applica lo stesso layout di base, tranne che lo stack e l'heap hanno dimensioni fisse.Tuttavia, in altri sistemi embedded (come quelli basati sui microcontrollori PIC Microchip), lo stack di programma è un blocco di memoria separato che non è indirizzabile dalle istruzioni di movimento dei dati e può essere modificato o letto solo indirettamente tramite istruzioni di flusso del programma (chiamata, ritorno, ecc.).Altre architetture, come i processori Intel Itanium, lo hanno pile multiple.In questo senso lo stack è un elemento dell'architettura della CPU.

Lo stack è una porzione di memoria che può essere manipolata tramite diverse istruzioni chiave del linguaggio assembly, come 'pop' (rimuove e restituisce un valore dallo stack) e 'push' (inserisce un valore nello stack), ma anche call ( chiamare una subroutine - questo spinge l'indirizzo a tornare nello stack) e return (ritorno da una subroutine - questo estrae l'indirizzo dallo stack e salta ad esso).È la regione di memoria sotto il registro del puntatore dello stack, che può essere impostata secondo necessità.Lo stack viene utilizzato anche per passare argomenti alle subroutine e anche per preservare i valori nei registri prima di chiamare le subroutine.

L'heap è una porzione di memoria che viene assegnata a un'applicazione dal sistema operativo, tipicamente tramite una chiamata di sistema come malloc.Nei sistemi operativi moderni questa memoria è un insieme di pagine a cui ha accesso solo il processo chiamante.

La dimensione dello stack viene determinata in fase di esecuzione e generalmente non aumenta dopo l'avvio del programma.In un programma C, lo stack deve essere sufficientemente grande da contenere ogni variabile dichiarata all'interno di ciascuna funzione.L'heap crescerà dinamicamente secondo necessità, ma alla fine è il sistema operativo a effettuare la chiamata (spesso aumenterà l'heap di più del valore richiesto da malloc, in modo che almeno alcuni malloc futuri non avranno bisogno di tornare al kernel per ottenere più memoria.Questo comportamento è spesso personalizzabile)

Poiché hai allocato lo stack prima di avviare il programma, non hai mai bisogno di malloc prima di poter utilizzare lo stack, quindi questo è un leggero vantaggio.In pratica, è molto difficile prevedere cosa sarà veloce e cosa sarà lento nei moderni sistemi operativi che dispongono di sottosistemi di memoria virtuale, perché il modo in cui vengono implementate le pagine e dove vengono archiviate è un dettaglio dell'implementazione.

Penso che molte altre persone ti abbiano dato risposte per lo più corrette su questo argomento.

Un dettaglio che però è sfuggito è che il "mucchio" dovrebbe in realtà essere chiamato "magazzino libero".Il motivo di questa distinzione è che il negozio gratuito originale è stato implementato con una struttura di dati nota come "mucchio binomiale". Per questo motivo, allocare dalle prime implementazioni di malloc ()/free () è stata l'allocazione da un mucchio.Tuttavia, al giorno d'oggi, la maggior parte dei negozi gratuiti sono implementati con strutture di dati molto elaborate che non sono cumuli binomiali.

Cos'è una pila?

Una pila è una pila di oggetti, in genere disposti in modo ordinato.

Enter image description here

Gli stack nelle architetture informatiche sono regioni di memoria in cui i dati vengono aggiunti o rimossi in modalità last-in-first-out.
In un'applicazione multi-thread, ogni thread avrà il proprio stack.

Cos'è un mucchio?

Un mucchio è una raccolta disordinata di cose ammucchiate a casaccio.

Enter image description here

Nelle architetture informatiche l'heap è un'area di memoria allocata dinamicamente che viene gestita automaticamente dal sistema operativo o dalla libreria del gestore della memoria.
La memoria sull'heap viene allocata, deallocata e ridimensionata regolarmente durante l'esecuzione del programma e ciò può portare a un problema chiamato frammentazione.
La frammentazione si verifica quando gli oggetti di memoria vengono allocati con piccoli spazi intermedi che sono troppo piccoli per contenere oggetti di memoria aggiuntivi.
Il risultato netto è una percentuale dello spazio heap non utilizzabile per ulteriori allocazioni di memoria.

Entrambi insieme

In un'applicazione multi-thread, ogni thread avrà il proprio stack.Ma tutti i diversi thread condivideranno l'heap.
Poiché i diversi thread condividono l'heap in un'applicazione multi-thread, ciò significa anche che deve esserci un certo coordinamento tra i thread in modo che non tentino di accedere e manipolare gli stessi pezzi di memoria nell'heap a lo stesso tempo.

Cos'è più veloce: lo stack o l'heap?E perché?

Lo stack è molto più veloce dell'heap.
Ciò è dovuto al modo in cui la memoria viene allocata nello stack.
Allocare memoria sullo stack è semplice come spostare il puntatore dello stack verso l'alto.

Per le persone nuove alla programmazione, probabilmente è una buona idea usare lo stack poiché è più semplice.
Poiché lo stack è piccolo, ti consigliamo di usarlo quando sai esattamente quanta memoria ti servirà per i tuoi dati o se sai che la dimensione dei tuoi dati è molto piccola.
È meglio utilizzare l'heap quando sai che avrai bisogno di molta memoria per i tuoi dati o semplicemente non sei sicuro di quanta memoria ti servirà (come con un array dinamico).

Modello di memoria Java

Enter image description here

Lo stack è l'area di memoria in cui vengono archiviate le variabili locali (inclusi i parametri del metodo).Quando si tratta di variabili oggetto, queste sono semplicemente riferimenti (puntatori) agli oggetti reali nell'heap.
Ogni volta che viene istanziata un oggetto, una parte della memoria heap viene messa da parte per contenere i dati (stato) di quell'oggetto.Poiché gli oggetti possono contenere altri oggetti, alcuni di questi dati possono infatti contenere riferimenti a quegli oggetti nidificati.

Puoi fare alcune cose interessanti con lo stack.Ad esempio, hai funzioni come alloca (supponendo che tu possa superare i copiosi avvertimenti riguardanti il ​​suo utilizzo), che è una forma di malloc che utilizza specificamente lo stack, non l'heap, per la memoria.

Detto questo, gli errori di memoria basati sullo stack sono tra i peggiori che abbia mai riscontrato.Se utilizzi la memoria heap e oltrepassi i limiti del blocco allocato, hai buone possibilità di attivare un errore di segmento.(Non al 100%:il tuo blocco potrebbe essere incidentalmente contiguo con un altro che hai precedentemente allocato.) Ma poiché le variabili create nello stack sono sempre contigue tra loro, scrivere fuori dai limiti può modificare il valore di un'altra variabile.Ho imparato che ogni volta che sento che il mio programma ha smesso di obbedire alle leggi della logica, probabilmente è un overflow del buffer.

Semplicemente, lo stack è il luogo in cui vengono create le variabili locali.Inoltre, ogni volta che si chiama una subroutine, il contatore del programma (puntatore alla successiva istruzione della macchina) e tutti i registri importanti, e talvolta i parametri vengono inseriti nello stack.Quindi qualsiasi variabile locale all'interno della subroutine viene inserita nello stack (e utilizzata da lì).Quando la subroutine termina, tutta quella roba viene tolta dallo stack.I dati del PC e del registro vengono recuperati e rimessi al loro posto, così il tuo programma può proseguire per la sua strada.

L'heap è l'area di memoria da cui vengono effettuate le allocazioni di memoria dinamica (chiamate esplicite "nuove" o "allocate").Si tratta di una speciale struttura dati in grado di tenere traccia di blocchi di memoria di varie dimensioni e del loro stato di allocazione.

Nei sistemi "classici" la RAM era disposta in modo tale che il puntatore dello stack iniziasse nella parte inferiore della memoria, il puntatore dell'heap iniziasse in alto e crescessero l'uno verso l'altro.Se si sovrappongono, la RAM è esaurita.Tuttavia, ciò non funziona con i moderni sistemi operativi multi-thread.Ogni thread deve avere il proprio stack e questi possono essere creati dinamicamente.

Da WikiAnwser.

Pila

Quando una funzione o un metodo chiama un'altra funzione che a sua volta chiama un'altra funzione, ecc., l'esecuzione di tutte quelle funzioni rimane sospesa finché l'ultima funzione non restituisce il suo valore.

Questa catena di chiamate di funzione sospese è lo stack, perché gli elementi nello stack (chiamate di funzione) dipendono l'uno dall'altro.

Lo stack è importante da considerare nella gestione delle eccezioni e nell'esecuzione dei thread.

Mucchio

L'heap è semplicemente la memoria utilizzata dai programmi per memorizzare le variabili.Gli elementi dell'heap (variabili) non hanno dipendenze tra loro e sono sempre accessibili in modo casuale in qualsiasi momento.

Pila

  • Accesso molto veloce
  • Non è necessario deallocare esplicitamente le variabili
  • Lo spazio è gestito in modo efficiente dalla CPU, la memoria non verrà frammentata
  • Solo variabili locali
  • Limite sulla dimensione dello stack (dipendente dal sistema operativo)
  • Le variabili non possono essere ridimensionate

Mucchio

  • È possibile accedere alle variabili a livello globale
  • Nessun limite alla dimensione della memoria
  • Accesso (relativamente) più lento
  • Nessun utilizzo efficiente dello spazio è garantito, la memoria potrebbe frammentarsi nel tempo man mano che i blocchi di memoria vengono allocati e quindi liberati
  • Devi gestire la memoria (sei responsabile dell'allocazione e della liberazione delle variabili)
  • Le variabili possono essere ridimensionate utilizzando realloc()

OK, semplicemente e in parole brevi, significano ordinato E non ordinato...!

Pila:Negli elementi impilati, le cose si sovrappongono l'una all'altra, il che significa che l'elaborazione sarà più veloce ed efficiente!...

Quindi c'è sempre un indice per indicare l'elemento specifico, inoltre l'elaborazione sarà più veloce, esiste anche una relazione tra gli elementi!...

Mucchio:Nessun ordine, l'elaborazione sarà più lenta e i valori saranno incasinati insieme senza alcun ordine o indice specifico...sono casuali e non c'è alcuna relazione tra loro...quindi il tempo di esecuzione e di utilizzo potrebbe variare...

Creo anche l'immagine qui sotto per mostrare come potrebbero apparire:

enter image description here

In breve

Uno stack viene utilizzato per l'allocazione della memoria statica e un heap per l'allocazione della memoria dinamica, entrambi archiviati nella RAM del computer.


In dettaglio

La pila

Lo stack è una struttura dati "LIFO" (last in, first out), che viene gestita e ottimizzata abbastanza attentamente dalla CPU.Ogni volta che una funzione dichiara una nuova variabile, viene "spinta" nello stack.Quindi ogni volta che una funzione esce, tutte le variabili inserite nello stack da quella funzione vengono liberate (vale a dire vengono cancellate).Una volta liberata una variabile di stack, quella regione di memoria diventa disponibile per altre variabili di stack.

Il vantaggio di utilizzare lo stack per archiviare le variabili è che la memoria viene gestita per te.Non è necessario allocare memoria manualmente o liberarla quando non ne hai più bisogno.Inoltre, poiché la CPU organizza la memoria dello stack in modo così efficiente, leggere e scrivere sulle variabili dello stack è molto veloce.

Si può trovarne di più Qui.


Il mucchio

L'heap è una regione della memoria del tuo computer che non viene gestita automaticamente e non è gestita così strettamente dalla CPU.È una regione di memoria più mobile (ed è più grande).Per allocare memoria sull'heap, è necessario utilizzare malloc() o calloc(), che sono funzioni C integrate.Una volta allocata la memoria sull'heap, sei responsabile dell'utilizzo di free() per deallocare quella memoria quando non ne hai più bisogno.

Se non lo fai, il tuo programma avrà quella che è conosciuta come una perdita di memoria.Cioè, la memoria sull'heap verrà comunque messa da parte (e non sarà disponibile per altri processi).Come vedremo nella sezione di debug, esiste uno strumento chiamato Valgrind che può aiutarti a rilevare perdite di memoria.

A differenza dello stack, l'heap non ha limiti di dimensione variabile (a parte le ovvie limitazioni fisiche del tuo computer).La memoria heap è leggermente più lenta per essere letta e scritta, perché è necessario utilizzare i puntatori per accedere alla memoria sull'heap.Tra poco parleremo dei puntatori.

A differenza dello stack, le variabili create nell'heap sono accessibili da qualsiasi funzione, ovunque nel programma.Le variabili heap hanno essenzialmente un ambito globale.

Si può trovarne di più Qui.


Le variabili allocate nello stack vengono archiviate direttamente nella memoria e l'accesso a questa memoria è molto veloce e la sua allocazione viene gestita al momento della compilazione del programma.Quando una funzione o un metodo chiama un'altra funzione che a sua volta chiama un'altra funzione, ecc., l'esecuzione di tutte quelle funzioni rimane sospesa finché l'ultima funzione non restituisce il suo valore.Lo stack è sempre riservato in ordine LIFO, il blocco prenotato più recentemente è sempre il blocco successivo da liberare.Ciò rende davvero semplice tenere traccia dello stack, liberare un blocco dallo stack non è altro che regolare un puntatore.

Le variabili allocate sull'heap hanno la memoria allocata in fase di esecuzione e l'accesso a questa memoria è un po' più lento, ma la dimensione dell'heap è limitata solo dalla dimensione della memoria virtuale.Gli elementi dell'heap non hanno dipendenze tra loro e sono sempre accessibili in modo casuale in qualsiasi momento.Puoi allocare un blocco in qualsiasi momento e liberarlo in qualsiasi momento.Ciò rende molto più complesso tenere traccia di quali parti dell'heap sono allocate o libere in un dato momento.

Enter image description here

Puoi utilizzare lo stack se sai esattamente quanti dati devi allocare prima della compilazione e non è troppo grande.Puoi utilizzare l'heap se non sai esattamente di quanti dati avrai bisogno in fase di esecuzione o se devi allocare molti dati.

In una situazione multi-thread ogni thread avrà il proprio stack completamente indipendente, ma condivideranno l'heap.Lo stack è specifico del thread e l'heap è specifico dell'applicazione.Lo stack è importante da considerare nella gestione delle eccezioni e nell'esecuzione dei thread.

Ogni thread riceve uno stack, mentre in genere esiste un solo heap per l'applicazione (sebbene non sia raro avere più heap per diversi tipi di allocazione).

Enter image description here

In fase di esecuzione, se l'applicazione necessita di più heap, può allocare memoria dalla memoria libera e se lo stack necessita di memoria, può allocare memoria dalla memoria libera allocata per l'applicazione.

Vengono forniti anche maggiori dettagli Qui E Qui.


Adesso rinveniamo le risposte alla tua domanda.

In che misura sono controllati dal sistema operativo o dal runtime del linguaggio?

Il sistema operativo alloca lo stack per ogni thread a livello di sistema quando viene creato il thread.In genere il sistema operativo viene chiamato dal Language Runtime per allocare l'heap per l'applicazione.

Si può trovarne di più Qui.

Qual è il loro ambito?

Già dato in alto.

"Puoi utilizzare lo stack se sai esattamente quanti dati devi allocare prima della compilazione e non è troppo grande.Puoi utilizzare l'heap se non sai esattamente di quanti dati avrai bisogno in fase di esecuzione o se devi allocare molti dati."

Di più si può trovare in Qui.

Cosa determina la dimensione di ciascuno di essi?

La dimensione dello stack è impostata da sistema operativo quando viene creato un thread.La dimensione dell'heap viene impostata all'avvio dell'applicazione, ma può aumentare man mano che lo spazio è necessario (l'allocatore richiede più memoria dal sistema operativo).

Cosa rende uno più veloce?

L'allocazione dello stack è molto più veloce poiché tutto ciò che fa realmente è spostare il puntatore dello stack.Utilizzando i pool di memoria, è possibile ottenere prestazioni comparabili dall'allocazione dell'heap, ma ciò comporta una leggera complessità aggiuntiva e i suoi grattacapi.

Inoltre, stack vs.l'heap non è solo una considerazione delle prestazioni;ti dice anche molto sulla durata prevista degli oggetti.

I dettagli possono essere trovati da Qui.

Negli anni '80, UNIX si propagò come conigli con le grandi aziende che si svilupparono per conto proprio.Exxon ne aveva uno così come dozzine di marchi perduti nella storia.Il modo in cui veniva organizzata la memoria era a discrezione dei molti implementatori.

Un tipico programma C è stato disposto in memoria con l'opportunità di aumentare modificando il valore BRK ().In genere, il mucchio era appena al di sotto di questo valore BRK e l'aumento del BRK ha aumentato la quantità di heap disponibile.

Il singolo stack era in genere un'area sotto il heap che era un tratto di memoria che non contiene nulla di valore fino alla parte superiore del prossimo blocco fisso di memoria.Questo blocco successivo era spesso un codice che poteva essere sovrascritto dai dati dello stack in uno dei famosi hack della sua era.

Un tipico blocco di memoria era BSS (un blocco di valori zero) che non era accidentalmente azzerato nell'offerta di un produttore.Un altro era DATA contenente valori inizializzati, incluse stringhe e numeri.Un terzo era CODICE contenente CRT (runtime C), main, funzioni e librerie.

L'avvento della memoria virtuale in UNIX modifica molti dei vincoli.Non vi è alcun motivo obiettivo per cui questi blocchi devono essere contigui o fissi di dimensioni o ordinati in un modo particolare.Naturalmente, prima di UNIX c'era Multics che non soffriva di questi vincoli.Ecco uno schema che mostra uno dei layout di memoria di quell'epoca.

A typical 1980s style UNIX C program memory layout

pila, mucchio E dati di ogni processo nella memoria virtuale:

stack, heap and static data

Un paio di centesimi:Penso che sarà bene disegnare la memoria grafica e più semplice:

This is my vision of process memory construction with simplification for more easy understanding wht happening


Frecce: mostrano dove crescono stack e heap, la dimensione dello stack del processo ha un limite, definito nel sistema operativo, i limiti della dimensione dello stack del thread in base ai parametri nell'API di creazione del thread in genere.L'heap in genere limita la dimensione massima della memoria virtuale in base al processo, ad esempio 2-4 GB per 32 bit.

Modo così semplice:l'heap del processo è generale per il processo e tutti i thread al suo interno, utilizzato per l'allocazione della memoria nel caso comune con qualcosa di simile malloc().

Lo stack è una memoria veloce per l'archiviazione di puntatori e variabili di ritorno di funzioni comuni, elaborati come parametri nella chiamata di funzione, variabili di funzioni locali.

Dato che alcune risposte sono state pignoli, contribuirò con il mio contributo.

Sorprendentemente, nessuno ha menzionato quel multiplo (cioènon correlato al numero di thread in esecuzione a livello di sistema operativo) gli stack di chiamate si trovano non solo in linguaggi esotici (PostScript) o piattaforme (Intel Itanium), ma anche in fibre, fili verdi e alcune implementazioni di coroutine.

Fibre, fili verdi e coroutine sono per molti versi simili, il che porta a molta confusione.La differenza tra fibre e fili verdi è che i primi utilizzano il multitasking cooperativo, mentre i secondi possono presentarne uno cooperativo o preventivo (o anche entrambi).Per la distinzione tra fibre e coroutine cfr Qui.

In ogni caso, lo scopo di entrambe le fibre, fili verdi e coroutine è di avere più funzioni eseguite contemporaneamente, ma non in parallelo (cfr questa domanda SO per la distinzione) all'interno di un singolo thread a livello di sistema operativo, trasferendo il controllo avanti e indietro l'uno dall'altro in modo organizzato.

Quando usi fibre, fili verdi o coroutine, tu Generalmente avere uno stack separato per funzione.(Tecnicamente, non solo uno stack ma un intero contesto di esecuzione è per funzione.Ancora più importante, i registri della CPU.) Per ogni thread ci sono tanti stack quante sono le funzioni in esecuzione contemporaneamente e il thread passa dall'esecuzione di ciascuna funzione in base alla logica del programma.Quando una funzione giunge al termine, il suo stack viene distrutto.COSÌ, il numero e la durata degli stack sono dinamici e non sono determinati dal numero di thread a livello di sistema operativo!

Tieni presente che ho detto "Generalmente avere uno stack separato per funzione".Ci sono entrambi pila E senza stack implementazioni delle routine.Le implementazioni C++ stackful più importanti sono Boost.Coroutine E Microsoft PPL'S async/await.(Tuttavia, C++ funzioni ripristinabili (aka"async E await"), che sono stati proposti in C++17, probabilmente utilizzano coroutine stackless.)

La proposta sulle fibre per la libreria standard C++ è imminente.Inoltre, ci sono alcune terze parti biblioteche.I fili verdi sono estremamente popolari in linguaggi come Python e Ruby.

Ho qualcosa da condividere, anche se i punti principali sono già stati trattati.

Pila

  • Accesso molto veloce.
  • Memorizzato nella RAM.
  • Le chiamate di funzione vengono caricate qui insieme alle variabili locali e ai parametri di funzione passati.
  • Lo spazio viene liberato automaticamente quando il programma esce dall'ambito.
  • Memorizzato nella memoria sequenziale.

Mucchio

  • Accesso lento rispetto allo Stack.
  • Memorizzato nella RAM.
  • Qui vengono memorizzate le variabili create dinamicamente, che successivamente richiedono la liberazione della memoria allocata dopo l'uso.
  • Memorizzato ovunque venga effettuata l'allocazione della memoria, accessibile sempre tramite puntatore.

Nota interessante:

  • Se le chiamate alle funzioni fossero state archiviate nell'heap, si sarebbero verificati 2 punti confusi:
    1. Grazie all'archiviazione sequenziale nello stack, l'esecuzione è più rapida.L'archiviazione nell'heap avrebbe comportato un enorme consumo di tempo, rallentando così l'esecuzione dell'intero programma.
    2. Se le funzioni fossero archiviate nell'heap (archiviazione disordinata puntata dal puntatore), non ci sarebbe stato modo di tornare all'indirizzo del chiamante (che lo stack fornisce a causa dell'archiviazione sequenziale in memoria).

Molte risposte sono corrette come concetti, ma dobbiamo notare che l'hardware ha bisogno di uno stack (ad es.microprocessore) per consentire la chiamata di subroutine (CALL in linguaggio assembly..).(I ragazzi dell'OOP lo chiameranno metodi)

Nello stack si salvano gli indirizzi di ritorno e call → push/ret → pop viene gestito direttamente in hardware.

Puoi usare lo stack per passare i parametri..anche se è più lento dell'utilizzo dei registri (direbbe un guru dei microprocessori o un buon libro sul BIOS degli anni '80...)

  • Senza pila NO il microprocessore può funzionare.(non possiamo immaginare un programma, anche in linguaggio assembly, senza subroutine/funzioni)
  • Senza il mucchio può.(Un programma in linguaggio assembly può funzionare senza, poiché l'heap è un concetto del sistema operativo, come malloc, cioè una chiamata OS/Lib.

L'utilizzo dello stack è più veloce poiché:

  • L'hardware e persino il push/pop sono molto efficienti.
  • malloc richiede l'accesso alla modalità kernel, l'utilizzo di lock/semaforo (o altre primitive di sincronizzazione) eseguendo del codice e la gestione di alcune strutture necessarie per tenere traccia dell'allocazione.

Oh!Così tante risposte e non credo che una di loro abbia capito bene...

1) Dove e cosa sono (fisicamente nella memoria di un computer reale)?

Lo stack è la memoria che inizia come l'indirizzo di memoria più alto allocato all'immagine del programma e da lì diminuisce di valore.È riservato ai parametri della funzione richiamata e a tutte le variabili temporanee utilizzate nelle funzioni.

Ci sono due cumuli:pubblici e privati.

L'heap privato inizia su un limite di 16 byte (per programmi a 64 bit) o ​​su un limite di 8 byte (per programmi a 32 bit) dopo l'ultimo byte di codice nel programma, quindi aumenta di valore da lì.Viene anche chiamato heap predefinito.

Se l'heap privato diventa troppo grande si sovrapporrà all'area dello stack, così come lo stack si sovrapporrà all'heap se diventa troppo grande.Poiché lo stack inizia da un indirizzo più alto e procede verso il basso fino all'indirizzo più basso, con un hacking adeguato puoi rendere lo stack così grande da sovraccaricare l'area dell'heap privato e sovrapporsi all'area del codice.Il trucco quindi è sovrapporre una parte sufficiente dell'area del codice da poter agganciare al codice.È un po' complicato da fare e rischi di mandare in crash il programma, ma è facile e molto efficace.

L'heap pubblico risiede nel proprio spazio di memoria al di fuori dello spazio dell'immagine del programma.È questa memoria che verrà trasferita sul disco rigido se le risorse di memoria scarseggiano.

2) In che misura sono controllati dal sistema operativo o dal Language Runtime?

Lo stack è controllato dal programmatore, l'heap privato è gestito dal sistema operativo e l'heap pubblico non è controllato da nessuno perché è un servizio del sistema operativo: fai richieste e vengono concesse o negate.

2b) Qual è il loro scopo?

Sono tutti globali per il programma, ma i loro contenuti possono essere privati, pubblici o globali.

2c) Cosa determina la dimensione di ciascuno di essi?

La dimensione dello stack e dell'heap privato sono determinati dalle opzioni di runtime del compilatore.L'heap pubblico viene inizializzato in fase di esecuzione utilizzando un parametro size.

2d) Cosa rende uno più veloce?

Non sono progettati per essere veloci, sono progettati per essere utili.Il modo in cui il programmatore li utilizza determina se sono "veloci" o "lenti"

RIF:

https://norasandler.com/2019/02/18/Write-a-Compiler-10.html

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap

https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top