Perché i tipi di valore sono memorizzati su stack?

Perché C # (.Net) preferisce lo stack per memorizzare i tipi di valore? Qual è la ragione principale alla base di questo design? Perché le operazioni di lettura / scrittura nello stack sfruttano meglio il processore della macchina?

Inoltre, forse puoi giustificare perché non gli altri?

Eric Lippert ne parla qui ; in primo luogo, non è corretto che “i tipi di valore siano memorizzati nello stack”. A volte lo sono, ma non come:

  • campi su una class
  • variabili catturate
  • variabili in un blocco iteratore

Quando possono essere memorizzati nello stack, è un modo conveniente di modellarne la durata, ma non è necessario memorizzarli nello stack. Ad esempio, potresti scrivere un compilatore + CLI che non ha uno stack.

C # non memorizza nulla nello stack. C # è un linguaggio di programmazione. Pertanto, una versione più corretta della tua domanda è: perché il compilatore Microsoft C # emette istruzioni CIL per allocare i tipi di valore nello stack?

Bene, prima, solo a volte. I seguenti non vanno in pila:

  1. Tipi di valore che sono campi in una class
  2. Tipi di valore in scatola
  3. Tipi di valore locali che sono variabili esterne di metodi anonimi
  4. Tipi di valore locali che sono variabili esterne dei blocchi iteratori

Secondo, quando è ansible è fatto perché è efficiente. Fondamentalmente nel modello di memoria CLR, la deallocazione dello stack è relativamente economica rispetto alla deallocazione nell’heap. Con le persone del posto di tipi di valore, puoi essere sicuro che nessun altro, tranne il locale, farà riferimento alla memoria in modo che tu possa farla franca usando lo stack invece dell’heap. Per i dettagli, vedi Eric Lippert .

Infine, ciò che rende speciali i tipi di valore è che hanno semantica del tipo valore (copia per valore), non che a volte sono allocati nello stack. Non c’è alcun requisito nella specifica C # che il compilatore emetta istruzioni per allocare i tipi di valore nello stack. Quello che richiede la specifica C # è che i tipi di valore abbiano semantica di tipo value.

Come indica @Akash, ha principalmente a che fare con la memoria. Durante la progettazione del CLR, è stato notato (la mia ipotesi era dall’esperienza con Java) che rappresentava piccoli tipi primitivi, poiché gli oggetti con le maniglie sottoposte al garbage collector causavano molti overhead di tracciamento. Quindi i progettisti volevano un object “leggero” che non necessitasse di essere rintracciato.

Non vi è alcun requisito specifico nella specifica CLI per le primitive da assegnare allo stack; è un artefatto dell’implementazione sulla macchina. Il bit essenziale è che il runtime sa dove le istanze sono dovute alla costruzione di modelli di memoria ben definiti (chiamati frame) piuttosto che all’indice del GC degli oggetti allocati. Su macchine x86 (e simili), questo può essere fatto in modo efficiente usando lo stack.

La tua affermazione non è completamente vera. Una versione migliore: C # memorizza le variabili locali nello stack.
E questo non è speciale o nuovo, (quasi) tutti i linguaggi di programmazione usano lo stack per le variabili locali e gli indirizzi di ritorno del metodo. C’è un supporto per questo fino all’hardware.

Inoltre, i Valuetypes possono essere variabili locali o campi all’interno di tipi di riferimento. Quindi i tipi di valore non vengono sempre memorizzati nello stack. Una dichiarazione più utile: i tipi di referencet non vengono mai memorizzati nello stack.

Quindi non concentrarti troppo sullo stack, è un dettaglio di implementazione. Ulteriori informazioni su valori e tipi di riferimento .

Operazione di stack o operazione di heap, entrambi saranno uguali a quando si accede a un indirizzo di memoria che si trova in due posizioni diverse.

I tipi di valore sono piccoli, int, byte ecc., Sono di piccole dimensioni e vengono citati molto frequentemente in termini di calcoli matematici. Dal momento che sono di dimensioni molto ridotte, da 4 a 16 byte max, (non si dovrebbero usare più di 16 byte in tipo valore per le migliori prestazioni), bind uno spazio così piccolo su heap e deallocating, garbage collection, ecc. Sarebbe molto costoso.

Ogni metodo che inseriamo, in media definiamo 10 tipi di valore locale per usarlo internamente, sarà molto oneroso in heap come tipi di riferimento.

Lo stack può crescere e restringersi facilmente (non le dimensioni dello stack, ma la porzione dello stack utilizzata per il metodo corrente !!) poiché i tipi di valori vengono semplicemente sfalsati dal puntatore dello stack e la loro allocazione e deallocazione è semplice come il suo semplice incremento e decremento sullo stackpointer dalla dimensione totale di tutti i tipi di valori utilizzati.

Laddove altro nel tipo di riferimento, ogni object di riferimento ha la propria allocazione e dimensionamento, inoltre CLR deve mantenere la tabella degli oggetti che è una specie di indice simile dei puntatori effettivi in ​​memoria per evitare overflow del buffer. Quindi un object che si utilizza (tipo di riferimento) ha effettivamente due spazio di archiviazione, una voce di indice nella tabella di riferimento CLR e lo spazio di memoria effettivo. Ecco perché è facile e veloce da memorizzare tipi di valore in pila.

Per il corretto funzionamento del programma, è importante che sia le entity framework di tipo di valore sia quelle di class sopravvivano a qualsiasi riferimento ad esse. Quando viene creato un object di tipo class, viene creato un riferimento che può essere liberamente copiato in qualsiasi ambito. Di conseguenza, è del tutto ansible che i riferimenti all’object continuino ad esistere anche dopo che l’ambito attuale è stato chiuso. Al contrario, quando viene creata una variabile di tipo valore, gli unici riferimenti che possono essere creati sono di un tipo di breve durata che scomparirà prima che l’ambito corrente venga chiuso. Il fatto che nessun riferimento possa esistere a una variabile di tipo valore quando l’attuale scope esita rende sicuro memorizzare tali variabili su uno stack.