it-swarm.it

Cosa e dove sono lo stack e l'heap?

I linguaggi di programmazione spiegano che i tipi di valore sono creati sullo stack , e i tipi di riferimento sono creati su heap , senza spiegare cosa sono queste due cose. Non ho letto una spiegazione chiara di questo. Capisco cos'è a stack. Ma, 

  • dove e cosa sono (fisicamente nella memoria di un vero computer)?
  • In che misura sono controllati dal sistema operativo o dal runtime della lingua?
  • Qual è il loro scopo?
  • Cosa determina la dimensione di ciascuno di essi?
  • Cosa rende più veloce? 
7473
mattshane

La pila è la memoria messa da parte come spazio per un thread di esecuzione. Quando viene chiamata una funzione, un blocco è riservato in cima allo stack per le variabili locali e alcuni dati contabili. Quando viene restituita la funzione, il blocco non viene utilizzato e può essere utilizzato la volta successiva che viene chiamata una funzione. Lo stack è sempre riservato in un ordine LIFO (ultimo nella prima uscita); il blocco riservato più di recente è sempre il blocco successivo da liberare. Questo rende molto semplice tenere traccia dello stack; liberare un blocco dallo stack non è altro che regolare un puntatore.

L'heap è la memoria riservata per l'allocazione dinamica. A differenza dello stack, non esiste un modello forzato 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; esistono molti allocatori di heap personalizzati disponibili per ottimizzare le prestazioni dell'heap per diversi modelli di utilizzo.

Ogni thread ottiene uno stack, mentre in genere c'è solo un 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 della lingua?

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 runtime della lingua per allocare l'heap per l'applicazione.

Qual è il loro scopo?

Lo stack è collegato a un thread, quindi quando il thread esce lo stack viene recuperato. L'heap viene in genere assegnato all'avvio dell'applicazione dal runtime ed è recuperato quando l'applicazione (tecnicamente di processo) viene chiusa.

Cosa determina la dimensione di ciascuno di essi? 

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

Cosa rende più veloce?

Lo stack è più veloce perché il pattern 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 a essere riutilizzato molto frequentemente, il che significa che tende ad essere mappato alla cache del processore, rendendolo molto veloce. Un altro problema di prestazioni per l'heap è che l'heap, essendo principalmente una risorsa globale, deve essere generalmente multi-threading sicuro, vale a dire che ogni allocazione e deallocazione deve essere tipicamente sincronizzata con "tutti" altri accessi al programma nel programma.

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

5529
Jeff Hill

Stack:

  • Memorizzato nel computer RAM proprio come l'heap.
  • Le variabili create nello stack andranno fuori campo e vengono automaticamente rilasciate.
  • Molto più veloce da allocare rispetto alle variabili sull'heap.
  • Implementato con una struttura di dati dello stack effettiva.
  • Memorizza i dati locali, restituisce gli indirizzi, utilizzati per il passaggio dei parametri.
  • Può avere uno stack overflow quando viene usato troppo stack (principalmente da ricorsione infinita o troppo profonda, allocazioni molto grandi).
  • I dati creati nello stack possono essere utilizzati senza puntatori.
  • Dovresti utilizzare lo stack se sai esattamente quanti dati hai bisogno di allocare prima della compilazione e non è troppo grande.
  • Di solito ha una dimensione massima già determinata all'avvio del programma.

Heap:

  • Memorizzato nel computer RAM proprio come lo stack.
  • In C++, le variabili sull'heap devono essere distrutte manualmente e non devono mai essere escluse dall'ambito. I dati vengono liberati con delete, delete[] o free.
  • Più lento da allocare rispetto alle variabili sullo stack.
  • Usato su richiesta per allocare un blocco di dati per l'utilizzo da parte del programma.
  • Può avere una frammentazione quando ci sono molte allocazioni e deallocazioni.
  • In C++ o C, i dati creati nell'heap saranno puntati da puntatori e allocati rispettivamente con new o malloc.
  • Può avere errori di allocazione se viene richiesto di allocare un buffer troppo grande.
  • Si utilizzerà l'heap se non si conosce esattamente la quantità di dati di cui si avrà bisogno in fase di esecuzione o se è necessario allocare molti dati.
  • Responsabile per 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;
2199
Brian R. Bondy

Il punto più importante è che heap e stack sono termini generici per i modi in cui la memoria può essere allocata. Possono essere implementati in molti modi diversi e i termini si applicano ai concetti di base.

  • In una pila di oggetti, gli oggetti si posizionano l'uno sull'altro nell'ordine in cui sono stati posizionati lì, e si può rimuovere solo quello superiore (senza rovesciare l'intero oggetto).

    Stack like a stack of papers

    La semplicità di uno stack è che non è necessario mantenere una tabella contenente un record di ciascuna sezione della memoria allocata; l'unica informazione di stato di cui hai bisogno è un singolo puntatore alla fine dello stack. Per allocare e de-allocare, basta incrementare e decrementare quel singolo puntatore. Nota: a volte può essere implementato uno stack per iniziare nella parte superiore di una sezione della memoria ed estendersi verso il basso anziché crescere verso l'alto.

  • In un heap, non esiste un ordine particolare per il modo in cui gli oggetti vengono posizionati. Puoi raggiungere e rimuovere gli articoli in qualsiasi ordine perché non c'è un chiaro elemento "top".

    Heap like a heap of licorice allsorts

    L'allocazione dell'heap richiede il mantenimento di una registrazione completa della memoria allocata e di ciò che non lo è, oltre a una manutenzione straordinaria per ridurre la frammentazione, trovare segmenti di memoria contigui abbastanza grandi da adattarsi alla dimensione richiesta e così via. La memoria può essere dislocata 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 intorno o la raccolta dei dati inutili, identificando in fase di esecuzione quando la memoria non è più in ambito e la rilascia. 

Queste immagini dovrebbero fare un buon lavoro nel descrivere i due modi di allocare e liberare memoria in uno stack e un heap. Yum!

  • In che misura sono controllati dal sistema operativo o dal runtime della lingua?

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

    Un heap è un termine generico utilizzato per qualsiasi memoria allocata in modo dinamico e casuale; cioè fuori uso. La memoria viene in genere allocata dal sistema operativo, con l'applicazione che chiama le funzioni API per eseguire questa allocazione. C'è un bel po 'di overhead richiesto nella gestione della memoria allocata dinamicamente, che viene solitamente gestita dal sistema operativo.

  • Qual è il loro scopo?

    Lo stack di chiamate è un concetto di basso livello che non si riferisce a "scope" nel senso della programmazione. Se si disassembla un po 'di codice, si vedranno i riferimenti relativi allo 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 restituita una funzione, qualsiasi elemento locale a quella funzione viene immediatamente liberato dallo stack. Funziona come ti aspetteresti che funzioni, visto come funzionano i tuoi linguaggi di programmazione. In un heap, è anche difficile da definire. L'ambito è quello che viene esposto dal sistema operativo, ma il linguaggio di programmazione probabilmente aggiunge le sue regole su cosa sia un "ambito" nell'applicazione. L'architettura del processore e il sistema operativo utilizzano l'indirizzamento virtuale, che il processore traduce in indirizzi fisici e ci sono errori di pagina, ecc. Essi tengono traccia di quali pagine appartengono a quali applicazioni. Non devi mai preoccuparti di questo, però, perché usi solo il metodo utilizzato dal linguaggio di programmazione per allocare e liberare memoria, e controlla gli errori (se l'assegnazione/liberazione fallisce per qualsiasi motivo).

  • Cosa determina la dimensione di ciascuno di essi?

    Di nuovo, dipende dalla lingua, dal compilatore, dal sistema operativo e dall'architettura. Solitamente uno stack viene pre-allocato, perché per definizione deve essere una memoria contigua (più su questo nell'ultimo paragrafo). Il compilatore di lingue o il sistema operativo determinano le sue dimensioni. Non si immagazzinano enormi blocchi di dati nello stack, quindi sarà abbastanza grande da non essere mai utilizzato completamente, tranne nei casi di ricorsione infinita indesiderata (quindi, "stack overflow") o altre decisioni di programmazione insolite.

    Un heap è un termine generico per tutto ciò che può essere assegnato 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 di solito non ti devi preoccupare molto di come funzioni in profondità, tranne che (nelle lingue dove ti permette) non devi usare la memoria non hai ancora assegnato o memoria che hai liberato.Cosa rende più veloce?.

  • What makes one faster?

    The stack is faster because all free memory is always contiguous. No list needs to be maintained of all the segments of free memory, just a single pointer to the current top of the stack. Compilers usually store this pointer in a special, fast register for this purpose. What's more, subsequent operations on a stack are usually concentrated within very nearby areas of memory, which at a very low level is good for optimization by the processor on-die caches.

1305
thomasrutter

(Ho spostato questa risposta da un'altra domanda che è stata più o meno una faccenda di questo.)

La risposta alla tua domanda è specifica dell'implementazione e può variare tra compilatori e architetture di 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 alla memoria fisica su richiesta).
  • In un ambiente multi-thread ogni thread avrà il proprio stack completamente indipendente ma condivideranno l'heap. L'accesso simultaneo deve essere controllato sull'heap e non è possibile in pila.

Il mucchio

  • L'heap contiene un elenco collegato di blocchi usati e liberi. Nuove allocazioni sullo heap (da new o malloc) sono soddisfatte creando un blocco adatto da uno dei blocchi liberi. Ciò richiede l'aggiornamento dell'elenco di blocchi nell'heap. Questo meta information sui blocchi sull'heap è anche memorizzato sull'heap spesso in una piccola area di fronte a ogni blocco.
  • Man mano che l'heap cresce, i nuovi blocchi vengono spesso assegnati da indirizzi inferiori a indirizzi più alti. Quindi puoi pensare all'heap come a heap dei blocchi di memoria che cresce di dimensioni man mano che la memoria viene allocata. Se l'heap è troppo piccolo per un'allocazione, la dimensione può essere aumentata spesso 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 vi sono molti piccoli blocchi liberi intercalati tra i blocchi utilizzati. Una richiesta di allocare un blocco di grandi dimensioni potrebbe non riuscire perché nessuno dei blocchi liberi è sufficientemente grande da soddisfare la richiesta di allocazione anche se la dimensione combinata dei blocchi liberi potrebbe essere abbastanza grande. Questo si chiama heap frammentation.
  • 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 che riduce effettivamente la frammentazione dell'heap.

The heap

Lo stack

  • Lo stack lavora spesso in stretto tandem con un registro speciale sulla CPU chiamato stack pointer. Inizialmente il puntatore dello stack punta in cima allo stack (l'indirizzo più alto nello stack).
  • La CPU ha istruzioni speciali per spingere i valori sullo stack e popping indietro dallo stack. Ogni Push memorizza il valore nella posizione corrente del puntatore dello stack e diminuisce il puntatore dello stack. A pop recupera il valore puntato dal puntatore dello stack e quindi aumenta il puntatore dello stack (non essere confuso dal fatto che aggiungendo un valore allo stack diminuisce il puntatore dello stack e rimuovendo un valore aumenta it Ricorda che lo stack aumenta 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 il puntatore dell'istruzione corrente, cioè l'indirizzo del codice in esecuzione nello stack. La CPU passa quindi alla funzione impostando il puntatore di istruzioni Sull'indirizzo della funzione chiamata. Successivamente, quando la funzione ritorna, il vecchio puntatore di istruzioni viene estratto dallo stack e l'esecuzione riprende dal codice subito dopo la chiamata alla funzione.
  • Quando viene immessa una funzione, il puntatore dello stack viene diminuito 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 messi in pila prima della chiamata alla funzione. Il codice nella funzione è quindi in grado di spostarsi verso l'alto dalla pila dal puntatore dello stack corrente per individuare questi valori.
  • Le chiamate alle funzioni di nidificazione funzionano come un incantesimo. Ogni nuova chiamata allocherà i parametri di funzione, l'indirizzo di ritorno e lo spazio per le variabili locali e questi record di attivazione possono essere impilati per le chiamate annidate e si svolgeranno nel modo corretto quando le funzioni ritornano.
  • Poiché lo stack è un blocco limitato di memoria, puoi causare un 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 al di sotto del fondo (l'indirizzo più basso) dello stack attiverà un trap o un'eccezione nella CPU. Questa condizione eccezionale può quindi essere catturata dal runtime e convertita in una sorta di eccezione di overflow dello stack.

The stack

È possibile allocare una funzione nell'heap anziché in uno stack?

No, i record di attivazione per le funzioni (ovvero le variabili locali o automatiche) sono allocati 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 in realtà dall'ambiente di runtime. C usa malloc e C++ usa new, ma molte altre lingue hanno la garbage collection.Tuttavia, lo stack è una funzionalità di livello più basso strettamente legata all'architettura del processore. Crescere l'heap quando non c'è abbastanza spazio non è troppo difficile poiché può essere implementato nella chiamata alla libreria che gestisce l'heap. Tuttavia, la crescita dello stack è spesso impossibile in quanto l'overflow dello stack viene scoperto solo quando è troppo tardi; e l'interruzione del thread di esecuzione è l'unica opzione valida.

However, the stack is a more low-level feature closely tied to the processor architecture. Growing the heap when there is not enough space isn't too hard since it can be implemented in the library call that handles the heap. However, growing the stack is often impossible as the stack overflow only is discovered when it is too late; and shutting down the thread of execution is the only viable option.

685
Martin Liversage

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 ha solo bisogno di durare fino a quando l'invocazione della funzione va in pila. L'heap viene utilizzato per le variabili la cui durata non è nota in anticipo, ma ci aspettiamo che durino un po '. Nella maggior parte delle lingue è fondamentale sapere al momento della compilazione quanto sia grande una variabile se vogliamo salvarla sullo stack. 

Gli oggetti (che variano di dimensioni man mano che li aggiorniamo) vanno in pila perché non sappiamo al momento della creazione quanto tempo dureranno. In molti linguaggi, l'heap è garbage collection per trovare oggetti (come l'oggetto cls1) che non hanno più riferimenti. 

In Java, la maggior parte degli oggetti va direttamente nell'heap. In linguaggi come C/C++, le strutture e le classi possono spesso rimanere in pila quando non si hanno a che fare con i puntatori.

Ulteriori informazioni possono essere trovate qui:

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

e qui: 

Creazione di oggetti nello stack e heap

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

ma sii consapevole che potrebbe contenere alcune inesattezze. 

379
Snowcrash

The Stack Quando si chiama una funzione, gli argomenti a tale funzione più qualche altro overhead vengono messi in pila. Alcune informazioni (come ad esempio dove andare al ritorno) sono anche memorizzate lì . Quando dichiari una variabile all'interno della tua funzione, quella variabile viene allocata nello stack. 

La deallocazione dello stack è piuttosto semplice perché si rilascia deallocate sempre nell'ordine inverso in cui viene allocata. Le risorse di stack vengono aggiunte non appena si inseriscono le funzioni, i dati corrispondenti vengono rimossi man mano che si esce. Ciò significa che tendi a rimanere all'interno di una piccola area dello stack, a meno che non chiami molte funzioni che chiamano molte altre funzioni (o che crei una soluzione ricorsiva).

The Heap L'heap è un nome generico per cui si inseriscono i dati che si creano al volo. Se non sai quante navicelle spaziali il tuo programma sta per creare, è probabile che tu usi l'operatore nuovo (o malloc o equivalente) per creare ciascuna astronave. Questa allocazione rimarrà per un po ', quindi è probabile che libereremo le cose in un ordine diverso da quello in cui le abbiamo create. 

Quindi, l'heap è molto più complesso, perché finiscono per essere regioni di memoria che sono inutilizzate interlacciate con blocchi che sono - la memoria si frammenta. Trovare una memoria libera delle dimensioni necessarie è un problema difficile. Questo è il motivo per cui l'heap dovrebbe essere evitato (anche se è ancora spesso usato).

Implementazione L'implementazione sia dello stack che dell'heap è in genere verso il runtime/OS. Spesso i giochi e le altre applicazioni che sono critiche per le prestazioni creano le proprie soluzioni di memoria che catturano una grande quantità di memoria dall'heap e quindi li distribuiscono internamente per evitare di fare affidamento sul sistema operativo per la memoria. 

Questo è pratico solo se l'utilizzo della memoria è alquanto diverso dalla norma, ad esempio per i giochi in cui si carica un livello in un'unica enorme operazione e si può buttare via tutto in un'altra enorme operazione.

Posizione fisica nella memoria Ciò è meno rilevante di quanto si pensi a causa di una tecnologia chiamata Memoria virtuale che fa pensare al programma che si ha accesso a un determinato indirizzo in cui i dati fisici si trovano altrove ( 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 non sono prevedibili (vale a dire l'impianto specifico) e francamente non sono importanti.

194
Tom Leys

Per chiarire, questa risposta ha informazioni errate ( thomas ha corretto la sua risposta dopo i commenti, figo :)). Altre risposte evitano semplicemente di spiegare cosa significa allocazione statica. Quindi spiegherò le tre principali forme di allocazione e in che modo si riferiscono in genere al mucchio, allo stack e al segmento di dati sottostante. Mostrerò anche alcuni esempi sia in C/C++ che in Python per aiutare le persone a capire.

Le variabili "statiche" (AKA allocate staticamente) non sono allocate nello stack. Non dare per scontato - molte persone lo fanno solo perché "statico" suona molto come "stack". In realtà non esistono né nello stack né nell'heap. Fanno parte del cosiddetto segmento di dati .

Tuttavia, generalmente è meglio considerare "scope" e "lifetime" piuttosto che "stack" e "heap".

L'ambito si riferisce a quali parti del codice possono accedere a una variabile. In generale, pensiamo a local scope (accessibile solo dalla funzione corrente) rispetto a global scope (accessibile ovunque) sebbene lo scope possa diventare molto più complesso.

La durata si riferisce a quando una variabile viene allocata e deallocata durante l'esecuzione del programma. Di solito pensiamo a static allocation (la variabile persisterà per l'intera durata del programma, rendendola utile per memorizzare le stesse informazioni tra più chiamate di funzione) rispetto a allocazione automatica (la variabile persiste solo durante una singola chiamata a una funzione, che lo rende utile per la memorizzazione di informazioni utilizzate solo durante la funzione e può essere scartato una volta terminato) rispetto a dynamic allocation (variabili la cui durata è definita in fase di esecuzione, anziché tempo di compilazione come statico o automatico ).

Sebbene la maggior parte dei compilatori e degli interpreti implementino questo comportamento allo stesso modo in termini di utilizzo di stack, heap, ecc., Un compilatore può talvolta interrompere queste convenzioni se lo desidera fino a quando il comportamento è 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 esistono nello stack. Come è stato sottolineato in alcuni commenti, sei libero di implementare un compilatore che non usi nemmeno uno stack o un heap, ma piuttosto altri meccanismi di archiviazione (raramente fatti, dato che stack e heap sono grandi per questo).

Fornirò del semplice codice C annotato per illustrare tutto questo. Il modo migliore per imparare è eseguire un programma con un debugger e osservare 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 perché è importante distinguere tra durata e ambito è che una variabile può avere un ambito locale ma una durata statica, ad esempio "someLocalStaticVariable" nell'esempio di codice sopra riportato. Tali variabili possono rendere le nostre abitudini di denominazione comuni ma informali molto confuse. Ad esempio quando diciamo "local" intendiamo di solito "localmente assegnata automaticamente alla variabile allocata" e quando diciamo globale di solito intendiamo "variabile allocata staticmente con scope globale". Sfortunatamente quando si tratta di cose come "variabili con allocazione statica con scope con scope del file" molte persone dicono semplicemente ... "huh ???".

Alcune delle scelte di sintassi in C/C++ esacerbano questo problema - per esempio molte persone pensano che le variabili globali non siano "statiche" a causa della sintassi mostrata sotto.

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

int main() {return 0;}

Si noti che inserendo la parola chiave "statico" nella dichiarazione precedente si impedisce a var2 di avere uno scope globale. Tuttavia, la var1 globale ha allocazione statica. Questo non è intuitivo! Per questo motivo, cerco di non utilizzare mai la parola "statico" quando descrivo l'ambito e dico 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 durata, "static" always significa che la variabile viene allocata all'avvio del programma e deallocata quando termina il programma.

Alcune persone pensano a questi concetti come specifici per C/C++. Non sono. Ad esempio, l'esempio di Python qui sotto illustra tutti e tre i tipi di allocazione (ci sono alcune sottili differenze nelle lingue interpretate che non entrerò 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 __== "__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
174
davec

Altri hanno risposto abbastanza bene agli ampi tratti, quindi aggiungerò alcuni dettagli.

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

  2. In C è possibile ottenere il beneficio dell'assegnazione della lunghezza variabile tramite l'uso di alloca , che alloca sullo stack, anziché allocare, che alloca sull'heap. Questa memoria non sopravviverà alla tua dichiarazione di ritorno, ma è utile per un buffer di scratch.

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

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

Altri hanno risposto direttamente alla tua domanda, ma quando provo a capire lo stack e l'heap, penso che sia utile considerare il layout di memoria di un processo UNIX tradizionale (senza gli allocatori di thread e mmap()-based). Il Memory Management Glossary pagina web ha uno schema 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 la chiamata di sistema brk() o sbrk(), mappando più pagine di memoria fisica nello spazio degli indirizzi virtuali del processo. 

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

127
bk1e

Lo stack è una porzione di memoria che può essere manipolata tramite diverse istruzioni di linguaggio Assembly chiave, come "pop" (rimuovere e restituire un valore dallo stack) e "Push" (spingere un valore nello stack), ma anche chiamare ( chiama una subroutine - questo spinge l'indirizzo per tornare allo stack) e ritorna (restituisce da una subroutine - questo apre l'indirizzo fuori dallo stack e lo salta). È la regione della memoria sotto il registro del puntatore dello stack, che può essere impostato secondo necessità. Lo stack viene anche utilizzato per passare argomenti a subroutine e anche per preservare i valori nei registri prima di chiamare le subroutine.

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

La dimensione dello stack è determinata in fase di runtime e generalmente non aumenta dopo l'avvio del programma. In un programma in C, lo stack deve essere abbastanza grande da contenere ogni variabile dichiarata all'interno di ciascuna funzione. L'heap crescerà dinamicamente secondo le necessità, ma il sistema operativo sta alla fine effettuando la chiamata (spesso farà crescere l'heap di oltre il valore richiesto da malloc, così che almeno alcuni mallocs futuri non avranno bisogno di tornare al kernel per avere più memoria.Questo comportamento è spesso personalizzabile)

Dato che hai allocato lo stack prima di avviare il programma, non hai mai bisogno di malloc prima di poter usare lo stack, quindi è un piccolo vantaggio. In pratica, è molto difficile prevedere cosa sarà veloce e cosa sarà lento nei moderni sistemi operativi che hanno sottosistemi di memoria virtuale, perché il modo in cui le pagine sono implementate e dove sono memorizzate è un dettaglio di implementazione. 

108
Daniel Papasian

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

Un dettaglio che è stato perso, tuttavia, è che "l'heap" dovrebbe essere chiamato "free store". La ragione di questa distinzione è che l'archivio gratuito originale è stato implementato con una struttura dati nota come "heap binomiale". Per questo motivo, l'allocazione dalle prime implementazioni di malloc ()/free () era l'allocazione da un heap. Tuttavia, in questo giorno moderno, la maggior parte dei negozi gratuiti sono implementati con strutture di dati molto elaborate che non sono cumuli binomiali.

107
Heath

Cos'è una pila?

Una pila è una pila di oggetti, di solito una che è ordinata in modo ordinato.

 Enter image description here

Le pile nelle architetture di calcolo sono regioni di memoria in cui i dati vengono aggiunti o rimossi in un modo ultimo-in-prima-out. 
In un'applicazione multi-thread, ogni thread avrà il proprio stack.

Cos'è un mucchio?

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

 Enter image description here

Nelle architetture di calcolo, l'heap è un'area di memoria allocata dinamicamente gestita automaticamente dal sistema operativo o dalla libreria del gestore di memoria. 
La memoria nell'heap viene allocata, deallocata e ridimensionata regolarmente durante l'esecuzione del programma e questo può portare a un problema chiamato frammentazione. 
La frammentazione si verifica quando gli oggetti di memoria vengono allocati con piccoli spazi intermedi troppo piccoli per contenere oggetti di memoria aggiuntivi. 
Il risultato netto è una percentuale dello spazio dell'heap che 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 a più thread, ciò significa anche che deve esserci un coordinamento tra i thread in modo che non provino ad accedere e a manipolare lo stesso/i pezzo/i di memoria nell'heap a lo stesso tempo.

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

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

Per le persone nuove alla programmazione, è probabilmente una buona idea usare lo stack dal momento che è più semplice. 
Poiché lo stack è di piccole dimensioni, è consigliabile utilizzarlo quando si conosce esattamente la quantità di memoria necessaria per i dati o se si conosce che la dimensione dei dati è molto ridotta. 
È meglio usare l'heap quando sai che avrai bisogno di molta memoria per i tuoi dati, o semplicemente non sei sicuro di quanta memoria hai bisogno (come con un array dinamico).

Modello di memoria Java

 Enter image description here

Lo stack è l'area della memoria in cui sono memorizzate le variabili locali (inclusi i parametri del metodo). Quando si tratta di variabili oggetto, questi sono semplicemente riferimenti (puntatori) agli oggetti reali nell'heap.
Ogni volta che un oggetto viene istanziato, un blocco di memoria heap viene messo 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 tali oggetti nidificati.

101
Shreyos Adikari

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

Detto questo, gli errori di memoria basati sullo stack sono alcuni dei peggiori che abbia mai visto. Se si utilizza la memoria heap e si superano i limiti del blocco assegnato, si ha una buona possibilità di attivare un errore di segmento. (Non 100%: il tuo blocco potrebbe essere incidentalmente contiguo con un altro che hai precedentemente assegnato.) Ma poiché le variabili create nello stack sono sempre contigue tra loro, la scrittura fuori limite può cambiare 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 buffer overflow.

87
Peter

Semplicemente, lo stack è dove vengono create le variabili locali. Inoltre, ogni volta che si chiama una subroutine, il contatore del programma (puntatore all'istruzione della macchina successiva) e qualsiasi registro importante, e talvolta i parametri vengono messi in pila. Quindi qualsiasi variabile locale all'interno della subroutine viene inserita nello stack (e utilizzata da lì). Quando finisce la subroutine, tutte le cose vengono rimosse dallo stack. Il PC e i dati del registro vengono recuperati e rimossi dov'era appena spuntato, quindi il tuo programma può andare avanti alla grande.

L'heap è l'area di memoria allocata alle allocazioni di memoria dinamica (chiamate "nuove" o "allocate" esplicite). È una struttura dati speciale in grado di tenere traccia dei blocchi di memoria di varie dimensioni e del loro stato di assegnazione.

Nei sistemi "classici" RAM è stato disposto in modo tale che il puntatore dello stack si avvii nella parte inferiore della memoria, il puntatore dell'heap si è avviato in alto e si sono sviluppati uno verso l'altro. Se si sovrappongono, sei fuori RAM. Tuttavia, ciò non funziona con i moderni sistemi operativi multi-thread. Ogni thread deve avere il proprio stack e quelli possono essere creati dinamicamente.

84
T.E.D.

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 a funzioni sospese è lo stack, poiché gli elementi nello stack (chiamate di funzione) dipendono l'uno dall'altro.

Lo stack è importante da considerare nella gestione delle eccezioni e nelle esecuzioni 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 possono sempre essere consultati casualmente in qualsiasi momento.

78
devXen

Stack

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

Heap

  • Le variabili sono accessibili a livello globale
  • Nessun limite per le dimensioni della memoria
  • (Relativamente) l'accesso più lento
  • Nessun uso efficiente garantito dello spazio, la memoria può frammentarsi nel tempo man mano che vengono allocati blocchi di memoria, quindi liberati
  • Devi gestire la memoria (hai il compito di allocare e liberare le variabili)
  • Le variabili possono essere ridimensionate usando realloc ()
50
unknown

OK, semplicemente e in parole brevi, significano ordinati e non ordinati ...!

Stack: negli oggetti stack, le cose si ottengono l'una sopra l'altra, significa che sarà più veloce ed efficiente da elaborare! ... 

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

Heap: Nessun ordine, l'elaborazione sarà più lenta ei valori vengono incasinati insieme senza un ordine o indice specifico ... ci sono casuali e non c'è alcuna relazione tra loro ... quindi l'esecuzione e il tempo di utilizzo potrebbero variare. ..

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

 enter image description here

38
Alireza

Negli anni '80, UNIX si è propagato come coniglietti con le grandi compagnie che hanno fatto il loro .. Exxon ne possedeva uno così come decine di nomi di marchi persi nella storia . La memoria era strutturata a discrezione dei molti implementatori.

Un tipico programma in C è stato pianificato in memoria con un'opportunità di aumentare cambiando il valore di brk () Tipicamente, l'HEAP era appena sotto questo valore di brack E l'aumento di brk aumentava la quantità di mucchio.

Il singolo STACK era tipicamente un'area sotto HEAP che era un tratto di memoria Che non conteneva nulla di valore fino alla cima del prossimo blocco fisso di memoria . Questo blocco successivo era spesso 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 era accidentalmente non 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 esiste una ragione oggettiva per cui questi blocchi debbano essere contigui, O fissati in dimensione, o ordinati in un modo particolare ora . Naturalmente, prima UNIX era Multics che non soffriva di questi vincoli . Ecco uno schema che mostra uno dei layout di memoria di quell'era.

A typical 1980s style UNIX C program memory layout

35
jlettvin

stack, heap e data di ogni processo nella memoria virtuale:

 stack, heap and static data

29
Yousha Aleayoub

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

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


Frecce: mostra dove cresce stack e heap, le dimensioni dello stack del processo hanno un limite, definito nel sistema operativo, i limiti delle dimensioni dello stack di thread in base ai parametri nell'API di creazione thread in genere. Heap di solito limita la dimensione massima della memoria virtuale del processo, ad esempio per 32 bit 2-4 GB.

Così semplice: l'heap del processo è generale per il processo e tutti i thread all'interno, utilizzando per l'allocazione della memoria nel caso comune con qualcosa come malloc ().

Lo stack è una memoria rapida per l'archiviazione nei richiami e indicatori di ritorno della funzione di caso comune, elaborati come parametri nella chiamata di funzione, variabili di funzione locali.

24
Maxim Akristiniy

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

Stack  

  • 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 da un ambito.
  • Memorizzato nella memoria sequenziale.

Heap

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

Nota interessante:

  • Se le chiamate alla funzione fossero state memorizzate in heap, si sarebbero ottenuti 2 punti disordinati:
    1. A causa della memorizzazione sequenziale nello stack, l'esecuzione è più veloce. Lo storage in heap avrebbe comportato un enorme consumo di tempo, rendendo l'intero programma più lento.
    2. Se le funzioni fossero archiviate nell'heap (memoria disordinata puntata da un puntatore), non sarebbe stato possibile tornare al richiamo dell'indirizzo del chiamante (quale stack dà a causa della memorizzazione sequenziale in memoria).
12

Molte risposte sono corrette come concetti, ma dobbiamo notare che uno stack è necessario per l'hardware (cioè il microprocessore) per consentire la chiamata di subroutine (CALL in linguaggio Assembly ..). (I ragazzi di OOP lo chiameranno methods)

Sulla pila si salvano gli indirizzi di ritorno e la chiamata → Push/ret → pop viene gestita direttamente nell'hardware.

Puoi usare lo stack per passare i parametri .. anche se è più lento di usare i registri (un guru del microprocessore direbbe o un buon libro del BIOS degli anni '80 ...)

  • Senza stack no microprocessore può funzionare. (non possiamo immaginare un programma, anche in linguaggio Assembly, senza subroutine/funzioni)
  • Può farlo senza l'heap. (Un programma di linguaggio Assembly può funzionare senza, poiché l'heap è un concetto di sistema operativo, come malloc, ovvero una chiamata OS/Lib.

L'utilizzo dello stack è più veloce come:

  • L'hardware e persino Push/pop sono molto efficienti.
  • malloc richiede l'accesso alla modalità kernel, usa lock/semaphore (o altre primitive di sincronizzazione) eseguendo del codice e gestendo alcune strutture necessarie per tenere traccia dell'allocazione.
8
ingconti

Wow! Così tante risposte e non penso che una di esse abbia capito bene ...

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

Lo stack è la memoria che inizia come il più alto indirizzo di memoria assegnato all'immagine del programma, quindi diminuisce di valore da lì. È riservato per i parametri di funzione chiamati e per tutte le variabili temporanee utilizzate nelle funzioni.

Ci sono due cumuli: pubblico e privato.

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ì. È anche chiamato l'heap predefinito.

Se l'heap privato diventa troppo grande si sovrapporrà all'area dello stack, così come lo stack si sovrappone all'heap se diventa troppo grande. Poiché lo stack inizia da un indirizzo più alto e scende fino a un indirizzo inferiore, con l'hacking corretto è possibile ottenere uno stack così grande da sovrascrivere l'area heap privata e sovrapporre l'area del codice. Il trucco è quindi quello di sovrapporre un numero sufficiente di aree del codice che è possibile collegare al codice. È un po 'complicato da fare e si rischia un crash del programma, ma è facile e molto efficace.

L'heap pubblico risiede nel proprio spazio di memoria al di fuori dello spazio immagine del programma. È questo il ricordo che verrà sottratto sull'hard disk se le risorse di memoria scarseggiano.

2) In che misura sono controllati dal sistema operativo o dal runtime della lingua?

Lo stack è controllato dal programmatore, l'heap privato è gestito dal sistema operativo e l'heap pubblico non è controllato da nessuno in quanto si tratta di un servizio OS: si effettuano 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?

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

2d) Cosa rende 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"

REF:

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

3
ar18

strategie di allocazione dello storage mediante la programmazione dei linguaggi 

MEMORIA IN C - LO STACK, IL HEAP E LO STATIC

enter image description here MEMORY IN C – THE STACK, THE HEAP, AND STATIC

  1. OP: in che misura sono controllati dal sistema operativo o dal runtime della lingua?

Voglio aggiungere alcune cose su questa importante domanda: 

OS e Common Language Runtime 

Componenti chiave di .NET Framework enter image description here Net Framework 4.5 Architecture enter image description here

Componenti di CLR Components of CLR enter image description here enter image description here enter image description here

0
leonidaa