2010-04-19 18 views
6

Mi sono imbattuto in qualche curioso comportamento riguardo alla garbage collection in .Net.Criteri per l'attivazione della garbage collection in .Net

Il seguente programma genererà una OutOfMemoryException molto rapidamente (dopo meno di un secondo su una macchina a 32 bit, 2 GB). Il finalizzatore Foo non viene mai chiamato.

class Foo 
{ 
    Guid guid = Guid.NewGuid(); 
    byte[] buffer = new byte[1000000]; 

    static Random rand = new Random(); 
    public Foo() 
    { 
     // Uncomment the following line and the program will run forever. 
     // rand.NextBytes(buffer); 
    } 

    ~Foo() 
    { 
     // This finalizer is never called unless the rand.NextBytes 
     // line in the constructor is uncommented. 
    } 

    static public void Main(string args[]) 
    { 
     for (; ;) 
     { 
      new Foo(); 
     } 
    } 
} 

Se la linea non sia commentata rand.nextBytes, viene attivata all'infinito, e il finalizzatore Foo viene richiamato regolarmente. Perché?

La mia ipotesi migliore è che nel primo caso, CLR o Windows VMM sono pigri nell'allocare la memoria fisica. Il buffer non viene mai scritto, quindi la memoria fisica non viene mai utilizzata. Quando lo spazio di indirizzi si esaurisce, il sistema si arresta in modo anomalo. In quest'ultimo caso, il sistema esaurisce la memoria fisica prima di esaurire lo spazio degli indirizzi, il GC viene attivato e gli oggetti vengono raccolti.

Tuttavia, ecco la parte che non ottengo. Supponendo che la mia teoria sia corretta, perché il GC non si attiva quando lo spazio degli indirizzi è basso? Se la mia teoria non è corretta, qual è la vera spiegazione?

risposta

1

Il codice viene eseguito su un valore stabile di 18 MB sulla mia macchina, con o senza tale linea (XP SP3 x86, .Net 3.5 SP1, dual core).

Probabilmente ciò che accade sulla vostra macchina è che quando la linea viene commentata, il programma trascorre la maggior parte del suo tempo ad allocare e riesce ad allocare troppa memoria prima che il thread del garbage collector abbia la possibilità di deallocarla. Quando decommentate quella linea, il programma dedica molto meno tempo all'allocazione e quindi non può allocare troppo prima dell'esecuzione del thread GC.

Provare a sostituire la riga commentata con Thread.Sleep(0); se non si blocca, probabilmente sono corretto.


Proprio come una nota a margine, non si dovrebbe mai fare affidamento sul finalizzatore - non è garantito di essere chiamato immediatamente quando l'oggetto è GC'ed, o addirittura per niente. Invece, nel codice vero e implementare l'interfaccia IDisposable, e utilizzare un finalizzatore solo se è estremamente importante cheDispose() essere chiamato, anche se il programmatore ha dimenticato (ad es. Liberando risorse di rete/file condivisi, ecc)

+0

I Non sono sicuro di avere la risposta giusta, ma mi metti su una pista interessante. Ho spento due Virtual PC in esecuzione (entrambi con 512 MB) e ho ottenuto il tuo comportamento. Quando ho caricato i VPC di backup, ho ottenuto il comportamento di arresto anomalo originale. –

+0

Ci sono anche alcune funzioni interessanti nella classe 'GC' da verificare, ma ancora una volta, nel codice reale queste non dovrebbero mai essere usate. –

+0

Con i VPC in esecuzione e Thread.Sleep (0) in posizione, il programma viene eseguito per sempre. Ora la domanda cambia ... Perché il GC non viene attivato automaticamente in caso di mancata allocazione? –