finalizzatore non chiamato dopo GC.Collect () [duplicate]
Penso che mi manchi qualcosa di fondamentale e spero che tu possa aiutare. Il codice seguente crea un oggetto, rimuove il riferimento e chiama il garbage collector. La mia aspettativa era che il finalizzatore di SomeClass sarebbe stato chiamato quando si trovava in Readline. Non è così. Ho provato a chiamare GC.Collect in un ciclo, aggiungendo alcune chiamate Sleep () per avviare il thread del finalizzatore. Non succede.
Solo quando il Main finisce il finalizzatore viene colpito, ma sorprendentemente viene colpito due volte. Cosa mi manca?
class Program
{
public static void Main(string[] args)
{
SomeClass some = new SomeClass("Hello World!");
some = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Done");
Console.ReadLine();
}
}
class SomeClass
{
string ss;
public SomeClass(string s) { ss = s; }
~SomeClass()
{
var hash = this.GetHashCode();
}
}
Addendum Esiste una differenza nell'esecuzione di un programma in modalità di debug rispetto alla modalità di rilascio. Il programma seguente produce in modalità di debug Start - Done - Finalize
mentre in modalità di rilascio mostra il file di registro Start - Finalize - Done
. Quest'ultimo è quello che mi aspettavo.
class Program
{
private static string logfile = @"c:\temp\log.txt";
public static void Main(string[] args)
{
File.WriteAllText(logfile, "Start\n");
SomeClass some = new SomeClass("Hello World!");
some = null;
GC.Collect();
GC.WaitForPendingFinalizers();
File.AppendAllText(logfile, "Done\n");
}
}
class SomeClass
{
private static string logfile = @"c:\temp\log.txt";
public string SomeString { get; set; }
public SomeClass(string s) { SomeString = s; }
~SomeClass()
{
File.AppendAllText(logfile, "Finalize\n");
}
}
Risposte
Gli oggetti raccolti in Garbage Collection diventano adatti per la finalizzazione e vengono inseriti in una coda di finalizzazione. Non c'è assolutamente alcuna garanzia che questi finalizzatori verranno mai eseguiti .
Ti reindirizzerò ai fantastici post di Eric Lippert su questo argomento, appropriatamente chiamati "Quando tutto quello che sai è sbagliato". In particolare:
Mito: l'impostazione di una variabile su null fa sì che il finalizzatore venga eseguito sull'oggetto a cui faceva riferimento in precedenza la variabile. L'impostazione di una variabile su null non fa accadere nulla immediatamente tranne la modifica del valore della variabile. Se la variabile era l'ultimo riferimento vivente all'oggetto in questione, allora quel fatto verrà scoperto quando il garbage collector esegue il collector per qualsiasi generazione in cui si trovava l'oggetto. (Se viene eseguito, potrebbe non esserlo. Non vi è alcuna garanzia che il GC esegue.)
E anche con GC.Collect()
:
Mito: la chiamata
GC.Collect()
provoca l'esecuzione dei finalizzatori. No, provoca una raccolta. Ciò potrebbe identificare gli oggetti candidati per la finalizzazione, ma non forza la pianificazione del thread del finalizzatore. Se è quello che vuoi, solo a scopo di test, per favore! - quindi chiamaGC.WaitForPendingFinalizers()
.
Quindi la migliore possibilità di successo è con GC.WaitForPendingFinalizers()
. Ma anche in questo caso, rimandandoti al resto del post ( e al suo seguito ), NON È GARANTITO LA CORSA DEI FINALIZZATORI . Non dipendere da quello.
Ho scritto una spiegazione più dettagliata su questa domanda in modo che tu possa usarla come punto di partenza per ulteriori ricerche.