2015-05-29 8 views
9

È noto che il garbage collector .NET non solo "elimina" gli oggetti nell'heap, ma combatte anche la frammentazione della memoria utilizzando la compattazione della memoria. Da quello che capisco, in pratica la memoria viene copiata in un nuovo posto, e il vecchio posto viene cancellato in qualche punto.In che modo il runtime .NET sposta la memoria?

La mia domanda è: come funziona?

Ciò di cui sono principalmente curioso è il fatto che il GC viene eseguito in un thread separato, il che significa che l'oggetto su cui stiamo lavorando può essere spostato dal GC mentre stiamo eseguendo il nostro codice.

dettagli tecnici della questione

Per illustrare, vorrei spiegare la mia domanda in modo più dettagliato:

class Program 
{ 
    private int foo; 
    public static void Main(string[] args) 
    { 
     var tmp = new Program(); // make an object 
     if (args.Length == 2) // depend the outcome on a runtime check 
     { 
      tmp.foo = 12;  // set value *** 
     } 
     Console.WriteLine(tmp.foo); 
    } 
} 

In questo piccolo esempio, creiamo un oggetto e impostare una variabile semplice su un oggetto. Il punto '***' è tutto ciò che conta per la domanda: se l'indirizzo di 'tmp' si sposta, 'foo' farà riferimento a qualcosa di non corretto e tutto si spezzerà.

Il garbage collector viene eseguito in una thread separata. Quindi, per quanto ne so, 'tmp' può essere spostato durante questa istruzione e 'foo' può finire con il valore errato. Ma in qualche modo, la magia accade e non lo fa.

Per quanto riguarda il disassembler, ho notato che il programma compilato prende davvero l'indirizzo di 'foo' e si muove nel valore '12:

000000ae 48 8B 85 10 01 00 00 mov   rax,qword ptr [rbp+00000110h] 
000000b5 C7 40 08 0C 00 00 00 mov   dword ptr [rax+8],0Ch 

ho più o meno aspettavo di vedere un puntatore indiretta qui, che può essere aggiornato, ma a quanto pare il GC funziona meglio di così.

Inoltre, non vedo alcuna sincronizzazione del thread che controlli se l'oggetto è stato spostato. Quindi, come fa il GC ad aggiornare lo stato nel thread in esecuzione?

Quindi, come funziona? E se il GC non muove questi oggetti, qual è la 'regola' che definisce se no o meno spostare gli oggetti?

+0

Si noti che le vecchie versioni di .NET utilizzavano un GC "stop-the-world", quindi i thread gestiti erano stati arrestati, GC era stato eseguito, i thread gestiti sono stati ripresi. – xanatos

+0

Sei consapevole del fatto che il GC riprende i dati solo quando la tua istanza di 'Program' è fuori ambito? Quindi, finché la tua app è in esecuzione, avrai sempre un valore corretto per "pippo". – HimBromBeere

risposta

5

Il GC .NET è (almeno in parte) un GC "stop-the-world": blocca i thread gestiti prima di eseguire il proprio lavoro, fa il suo lavoro, quindi riavvia i thread gestiti.

Il GC "Workstation" può essere concomitante (quindi parzialmente non stop-the-world), ma nota a https://msdn.microsoft.com/library/ee851764.aspx.

Quando si utilizza workstation raccolta dei rifiuti con la raccolta dei rifiuti concomitante, gli oggetti recuperati non vengono compattati, per cui la dimensione heap può essere lo stesso o più grande (la frammentazione può far sembrare di essere più grande).

Si noti che con tutti i GC, gen0 e gen1 sono sempre stop-the-world. In questo modo possono spostare i blocchi di memoria senza problemi. Solo gen2 può essere fatto in background da alcuni GC con alcune configurazioni (questo link, l'informazione è un po 'frammentata tutta intorno alla pagina), quindi c'è sempre un momento "the-world-is-stopped" dove la memoria che è stata liberato può essere compattato.

+0

Si noti che il GC concorrente è stato effettivamente sostituito con GC "di sfondo" che inizia con .Net 4. [Alcuni dettagli qui] (https://msdn.microsoft.com/en-us/library/ee787088%28v=vs.110%29 .aspx) –

+0

@MatthewWatson E da 4.5 anche il server ha un GC "in background". Ancora gen0 e gen1 sono sempre "stop-the-world". Solo gen2 funziona da un altro thread (s) – xanatos

+0

@xanatos Ciò significherebbe che quando "fermi il mondo" sai esattamente dove si trova il contatore del programma, quali sono i locals nei registri, quali sono i locali - ecc. Dopo GC continui ripristinando tutto questo (ecc.) - ancora peggio: stai facendo tutto questo su una CPU diversa da quella in cui sei attualmente in esecuzione? Non saprei nemmeno come iniziare se volessi implementarlo ... Come diavolo fai qualcosa del genere? – atlaste

Problemi correlati