In che modo la memoria gestita .net gestisce i tipi di valore all'interno degli oggetti?

StackOverflow https://stackoverflow.com/questions/24829

  •  09-06-2019
  •  | 
  •  

Domanda

public class MyClass
{
    public int Age;
    public int ID;
}

public void MyMethod() 
{
    MyClass m = new MyClass();
    int newID;
}

Per quanto mi risulta, è vero quanto segue:

  1. Il riferimento m risiede nello stack e esce dall'ambito quando MyMethod() esce.
  2. Il tipo di valore newID risiede nello stack e esce dall'ambito quando MyMethod() esce.
  3. L'oggetto creato dall'operatore new risiede nell'heap e diventa recuperabile dal GC quando MyMethod() esce, presupponendo che non esista nessun altro riferimento all'oggetto.

Ecco la mia domanda:

  1. I tipi di valore all'interno degli oggetti vivono nello stack o nell'heap?
  2. I tipi di valore boxing/unboxing in un oggetto sono un problema?
  3. Esistono risorse dettagliate ma comprensibili su questo argomento?

Logicamente, penso che i tipi di valore all'interno delle classi sarebbero nell'heap, ma non sono sicuro che debbano essere inscatolati per arrivarci.

Modificare:

Lettura consigliata per questo argomento:

  1. CLR tramite C# di Jeffrey Richter
  2. .NET essenziale di Don Box
È stato utile?

Soluzione

Valori del tipo valore per una classe Avere per convivere con l'istanza dell'oggetto nell'heap gestito.Lo stack del thread per un metodo vive solo per la durata di un metodo;come può il valore persistere se esiste solo all'interno di quello stack?

La dimensione dell'oggetto di una classe nell'heap gestito è la somma dei relativi campi di tipo valore, puntatori di tipo riferimento e variabili generali CLR aggiuntive come l'indice del blocco di sincronizzazione.Quando si assegna un valore al campo del tipo valore di un oggetto, CLR copia il valore nello spazio allocato all'interno dell'oggetto per quel campo particolare.

Prendiamo ad esempio una classe semplice con un singolo campo.

public class EmbeddedValues
{
  public int NumberField;
}

E con esso, una semplice lezione di prova.

public class EmbeddedTest
{
  public void TestEmbeddedValues()
  {
    EmbeddedValues valueContainer = new EmbeddedValues();

    valueContainer.NumberField = 20;
    int publicField = valueContainer.NumberField;
  }
}

Se si utilizza MSIL Disassembler fornito da .NET Framework SDK per visualizzare il codice IL per EmbeddedTest.TestEmbeddedValues()

.method public hidebysig instance void  TestEmbeddedValues() cil managed
{
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] class soapextensions.EmbeddedValues valueContainer,
           [1] int32 publicField)
  IL_0000:  nop
  IL_0001:  newobj     instance void soapextensions.EmbeddedValues::.ctor()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.s   20
  IL_000a:  stfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_000f:  ldloc.0
  IL_0010:  ldfld      int32 soapextensions.EmbeddedValues::NumberField
  IL_0015:  stloc.1
  IL_0016:  ret
} // end of method EmbeddedTest::TestEmbeddedValues

Notare che viene detto al CLR stfld il valore caricato di "20" nello stack nella posizione del campo NumberField di EmbeddValues ​​caricato, direttamente nell'heap gestito.Allo stesso modo, quando si recupera il valore, utilizza ldfld istruzione per copiare direttamente il valore da quella posizione heap gestita nello stack di thread.Con questo tipo di operazioni non avviene alcun box/unboxing.

Altri suggerimenti

  1. Tutti i riferimenti o i tipi di valore posseduti da un oggetto risiedono nell'heap.
  2. Solo se stai trasmettendo int a Objects.

La migliore risorsa che ho visto per questo è il libro CLR via C# di Jeffrey Richter.Vale la pena leggerlo se ti occupi di sviluppo .NET.Sulla base di quel testo, da quanto ho capito, i tipi di valore all'interno di un tipo di riferimento risiedono nell'heap incorporato nell'oggetto principale.I tipi di riferimento sono Sempre sul mucchio.Boxing e unboxing non sono simmetrici.La boxe può essere una preoccupazione più grande dell'unboxing.Boxe Volere richiedono la copia del contenuto del tipo di valore dallo stack all'heap.A seconda della frequenza con cui ciò accade, potrebbe non avere senso avere una struttura invece di una classe.Se disponi di codice critico per le prestazioni e non sei sicuro che si stia verificando il boxing e l'unboxing, utilizza uno strumento per esaminare il codice IL del tuo metodo.Vedrai le parole box e unbox nell'IL.Personalmente, misurerei le prestazioni del mio codice e solo allora vedrei se questo è motivo di preoccupazione.Nel tuo caso non credo che questo sarà un problema così critico.Non dovrai copiare dallo stack all'heap (box) ogni volta che accedi a questo tipo di valore all'interno del tipo di riferimento.Questo scenario è quello in cui la boxe diventa un problema più significativo.

  • Risposta n. 1:Mucchio.Parafrasando Don Box dal suo eccellente "Essential .Net Vol 1"

I tipi di riferimento (RT) producono sempre istanze allocate nell'heap. Al contrario, i tipi di valore (VT) dipendono dal contesto: se una var locale è un VT, CLR alloca memoria nello stack.Se un campo in una classe è membro di un VT, CLR alloca memoria per l'istanza come parte del layout dell'oggetto/tipo in cui il campo è dichiarato.

  • Risposta n. 2:NO.Il boxing si verifica solo quando si accede a una struttura tramite un riferimento oggetto/puntatore interfaccia. obInstance.VT_typedfield non boxerà.

    Le variabili RT contengono l'indirizzo dell'oggetto a cui si riferiscono.2 RT var può puntare allo stesso oggetto. Al contrario, le variabili VT sono le istanze stesse.2 VT var non può puntare allo stesso oggetto (struct)

  • Risposta n. 3:Essential .net di Don Box / CLR di Jeffrey Richter tramite C#.Ho una copia del precedente...sebbene quest'ultimo potrebbe essere più aggiornato per le revisioni .Net

I tipi di valore all'interno degli oggetti vivono nello stack o nell'heap?

Nel mucchio.Fanno parte dell'allocazione dell'impronta dell'oggetto, proprio come lo sarebbero i puntatori per contenere i riferimenti.

I tipi di valore boxing/unboxing in un oggetto sono un problema?

Non c'è boxe qui.

Esistono risorse dettagliate ma comprensibili su questo argomento?

+1 voto per il libro di Richter.

Una variabile o un'altra posizione di archiviazione di un tipo di struttura è un'aggregazione dei campi dell'istanza pubblica e privata di quel tipo.Dato

struct Foo {public int x,y; int z;}

una dichiarazione Foo bar; causerà bar.x, bar.y, E bar.z da riporre ovunque bar verrà memorizzato.Aggiungendo tale dichiarazione di bar a una classe, dal punto di vista del layout di archiviazione, equivarrà ad aggiungerne tre int campi.Infatti, se uno non avesse mai fatto nulla con bar tranne accedere ai suoi campi, i campi di bar si comporterebbe allo stesso modo di tre campi bar_x, bar_y, E bar_cantaccessthis_z [per accedere all'ultimo sarebbe necessario fare delle cose con bar oltre ad accedere ai suoi campi, ma occuperebbe spazio indipendentemente dal fatto che venga effettivamente utilizzato o meno per qualcosa].

Riconoscere le posizioni di archiviazione di tipo struttura come aggregazioni di campi è il primo passo per comprendere le strutture.Cercare di vederli come portatori di qualche tipo di oggetto potrebbe sembrare "più semplice", ma non corrisponde al modo in cui funzionano effettivamente.

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