finaliseur non appelé après GC.Collect () [duplicate]
Je pense qu'il me manque quelque chose de fondamental et j'espère que vous pourrez m'aider. Le code ci-dessous crée un objet, supprime la référence et appelle le garbage collector. Je m'attendais à ce que le finaliseur de SomeClass soit appelé en se tenant dans Readline. Ce n'est pas le cas. J'ai essayé d'appeler GC.Collect dans une boucle, en ajoutant des appels Sleep () pour démarrer le thread du finaliseur. Cela n'arrive pas.
Ce n'est qu'à la fin du Main que le finaliseur est touché, mais étonnamment, il est frappé deux fois. Qu'est-ce que je rate?
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 Il existe une différence entre l'exécution d'un programme en mode débogage et en mode version. Le programme ci-dessous produit en mode débogage Start - Done - Finalize
tandis qu'en mode version, le fichier journal s'affiche Start - Finalize - Done
. Ce dernier est ce à quoi je m'attendais.
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");
}
}
Réponses
Les objets qui sont récupérés de la mémoire deviennent appropriés pour la finalisation et sont placés dans une file d'attente de finalisation. Il n'y a absolument aucune garantie que ces finaliseurs seront jamais exécutés .
Je vais vous rediriger vers les excellents messages d'Eric Lippert à ce sujet, appelés à juste titre «Quand tout ce que vous savez est faux». En particulier:
Mythe: la définition d'une variable sur null entraîne l'exécution du finaliseur sur l'objet précédemment référencé par la variable. La définition d'une variable sur null ne provoque rien immédiatement, sauf la modification de la valeur de la variable. Si la variable était la dernière référence vivante à l'objet en question, ce fait sera découvert lorsque le ramasse-miettes exécutera le collecteur quelle que soit la génération dans laquelle se trouvait l'objet (s'il s'exécute du tout, ce n'est peut-être pas le cas. Il n'y a aucune garantie que le GC fonctionne.)
Et même avec GC.Collect()
:
Mythe: L'appel
GC.Collect()
entraîne l'exécution des finaliseurs. Non, cela provoque une collecte. Cela peut identifier les objets candidats à la finalisation, mais cela ne force pas la planification du thread du finaliseur. Si c'est ce que vous voulez - uniquement à des fins de test, s'il vous plaît! - puis appelezGC.WaitForPendingFinalizers()
.
Donc, la meilleure chance de succès est avec GC.WaitForPendingFinalizers()
. Mais même dans ce cas, en vous référant au reste de l'article ( et à sa suite ), LES FINALISATEURS NE SONT PAS GARANTIS DE FONCTIONNER . Ne comptez pas sur ça.
J'ai écrit une explication plus détaillée sur cette question afin que vous puissiez l'utiliser comme point de départ pour d'autres recherches.