2012-02-29 19 views
10

Sono curioso di sapere come i riferimenti all'oggetto C# sono rappresentati in memoria in fase di esecuzione (in .NET CLR). Alcune domande che ti vengono in mente sono:come vengono rappresentati i riferimenti all'oggetto C# in memoria/in fase di esecuzione (nel CLR)?

  1. Quanta memoria occupa un riferimento a un oggetto? Differisce se definito nell'ambito di una classe rispetto all'ambito di un metodo? Il luogo in cui vive differisce in base a questo ambito (stack vs heap)?

  2. Quali sono i dati effettivi gestiti all'interno di un riferimento a un oggetto? È semplicemente un indirizzo di memoria che punta all'oggetto a cui si riferisce o c'è di più? Questo differisce in base al fatto che sia definito nell'ambito di una classe o di un metodo?

  3. Stesse domande come sopra, ma questa volta quando si parla di un riferimento a un riferimento, come in quando un riferimento a un oggetto viene passato a un metodo per riferimento. Come cambiano le risposte all'1 e al 2?

+3

Si noti che queste domande sono tutti dettagli di implementazione (che sono soggetti a modifiche) e non in realtà su C#, ma piuttosto su .NET CLR. – dlev

+0

Chopperdave, una domanda interessante ma voglio chiederti se stai chiedendo cosa intendevi: un riferimento a un oggetto è in gran parte un puntatore e questo è solo un "numero" a seconda dell'architettura del sistema su cui è in esecuzione il tuo codice. Se stai chiedendo come funzionano le allocazioni di .Net Heap, questa è una bestia completamente diversa. –

+0

Volevo solo aggiungere, nessun insulto inteso qui, non sto cercando di implicare che non sai cosa intendi - Il fatto è che in .Net questa è una domanda ambigua e aiuterà i futuri utenti sullo Stack Overflow per sapere con precisione di quale contesto stiamo parlando. –

risposta

12

Questa risposta è più facilmente comprensibile se si capisce puntatori C/C++. Un puntatore è semplicemente l'indirizzo di memoria di alcuni dati.

  1. Un riferimento all'oggetto dovrebbe essere la dimensione di un puntatore, che normalmente è di 4 byte su una CPU 32-bit, e 8 byte su una CPU a 64 bit. È lo stesso indipendentemente da dove è definito. Dove vive dipende da dove è definito. Se è un campo di una classe, risiederà nell'heap nell'oggetto di cui fa parte. Se si tratta di un campo statico, si trova in una sezione speciale dell'heap che non è soggetta alla garbage collection. Se è una variabile locale, vive in pila.

  2. Un riferimento oggetto è semplicemente un puntatore, che può essere visualizzato come int o long contenente l'indirizzo dell'oggetto in memoria. È lo stesso indipendentemente da dove è definito.

  3. Questo è implementato come puntatore a un puntatore. I dati sono gli stessi - solo un indirizzo di memoria. Tuttavia, non esiste alcun oggetto all'indirizzo di memoria indicato. Invece, c'è un altro indirizzo di memoria, che è il riferimento originale all'oggetto. Questo è ciò che consente di modificare un parametro di riferimento. Normalmente, un parametro scompare quando il suo metodo viene completato. Poiché il riferimento all'oggetto non è un parametro, le modifiche a questo riferimento rimarranno. Il riferimento a un riferimento scompare, ma non il riferimento. Questo è lo scopo per il passaggio dei parametri di riferimento.

Una cosa che si dovrebbe sapere, i tipi di valore sono memorizzati sul posto (non v'è alcun indirizzo di memoria, invece sono memorizzati direttamente dove sarebbe l'indirizzo di memoria - vedi # 1). Quando vengono passati a un metodo, viene eseguita una copia e tale copia viene utilizzata nel metodo. Quando vengono passati per riferimento, viene passato un indirizzo di memoria che individua il tipo di valore in memoria, consentendone la modifica.

Modifica: come sottolineato da Dlev, queste risposte non sono la regola dura e veloce, poiché non esiste una regola che dice che è così che deve essere. .NET è libero di implementare queste domande come vuole. Questo è il modo più probabile per implementarlo, dato che questo è il modo in cui la CPU Intel lavora internamente, quindi l'uso di qualsiasi altro metodo sarebbe probabilmente inefficiente.

Spero di non averti confuso troppo, ma sentiti libero di chiedere se hai bisogno di chiarimenti.

+0

I tipi di valore non vengono sempre memorizzati nello stack. –

+2

"i tipi di valore sono memorizzati nello stack": non è vero.I tipi di valore ** possono ** essere memorizzati nello stack, ma non è sempre il caso. Per esempio. un campo di tipo valore in un tipo di riferimento è memorizzato nell'heap, nell'oggetto a cui appartiene. +1 in ogni caso, perché la tua risposta è per lo più corretta ... –

+0

@ThomasLevesque: Rimosso il riferimento ai tipi di valore sullo stack. Grazie! –

13

.NET Heaps and Stacks Questo è un trattamento completo di come funzionano stack e heap.

C# e molti altri linguaggi OOP heap-utilizzando, in generale, l'uso di riferimento-parlare non gestisce i puntatori per i riferimenti in questo contesto (C# è anche in grado di utilizzare puntatori!) Pointer analogie funzionano per alcuni concetti generali, ma questo concettuale il modello si rompe per domande come questa. Guarda l'eccellente post di Eric Lippert su questo argomento Handles are Not Addresses

Non è appropriato dire che Handle ha le dimensioni di un puntatore. (sebbene possa coincidere a essere lo stesso) Le maniglie sono alias per gli oggetti, non è richiesto che siano un indirizzo formale di un oggetto.

In questo caso il CLR capita di utilizzare indirizzi reali per le maniglie: dal link qui sopra:

... il CLR in realtà fa implementare riferimenti a oggetti gestiti come indirizzi per gli oggetti di proprietà del garbage collector , ma questo è un dettaglio di implementazione .

Quindi sì una maniglia è probabilmente 4 byte su un'architettura a 32 bit, e 8 byte su un'architettura a 64 byte, ma questo non è un "certo", ed è non direttamente a causa di puntatori. Vale la pena notare che in base all'implementazione del compilatore e allo gli intervalli di indirizzi utilizzati alcuni tipi di puntatori possono essere diversi nella dimensione.

Con tutto questo contesto è possibile probabilmente modellarlo con un'analogia del puntatore, ma è importante rendersi conto che i manici non devono essere indirizzi. Il CLR potrebbe scegliere di cambiare questo se lo volesse in futuro e i consumatori del CLR non dovrebbero saperlo meglio.

Una trasmissione finale di questo punto sottile:

Si tratta di un C# Pointer:

int* myVariable; 

Si tratta di un C# Maniglia:

object myVariable; 

Essi non sono la stessa cosa.

Puoi fare cose come la matematica sui puntatori, che non dovresti fare con Handles. Se il tuo handle è implementato come un puntatore e lo usi come se fosse un puntatore, stai usando male l'Handle in un modo che potrebbe metterti nei guai in seguito.

+3

Questo non è un puntatore C# perché è illegale creare un puntatore a un tipo di riferimento gestito. 'int *' sarebbe un esempio di un tipo di puntatore C#. –

Problemi correlati