Ok, il est connu que GC appelle implicitement les méthodes Finalize
sur des objets lorsqu'il identifie cet objet en tant que garbage. Mais que se passe-t-il si je fais un GC.Collect()
? Les finaliseurs sont-ils encore exécutés? Une question stupide peut-être, mais quelqu'un m'a posé cette question et j'ai répondu «Oui», puis j'ai pensé: « Est-ce que c'est tout à fait correct? »
En fait, la réponse "ça dépend". En réalité, il existe un thread dédié qui exécute tous les finaliseurs. Cela signifie que l'appel à GC.Collect
n'a déclenché que ce processus et que l'exécution de tous les finaliseurs serait appelée de manière asynchrone.
Si vous voulez attendre que tous les finaliseurs soient appelés, vous pouvez utiliser le truc suivant:
GC.Collect();
// Waiting till finilizer thread will call all finalizers
GC.WaitForPendingFinalizers();
Oui, mais pas tout de suite. Cet extrait provient de Garbage Collection: gestion automatique de la mémoire dans Microsoft .NET Framework (MSDN Magazine) (*)
"Lorsqu'une application crée un nouvel objet, l'opérateur new alloue La mémoire du tas. Si le type de l'objet contient une méthode Finalize , Un pointeur sur l'objet est placé dans la file d'attente de finalisation La file d'attente de finalisation est une structure de données interne contrôlée Par le récupérateur de mémoire. Chaque entrée de la file d'attente pointe vers un objet Dont la méthode Finalize doit être appelée avant que la mémoire de l'objet .__ puisse être récupérée.
Quand un GC se produit ... le garbage collector analyse la finalisation file d'attente à la recherche de pointeurs sur ces objets. Lorsqu'un pointeur est trouvé, le pointeur est supprimé de la file d'attente de finalisation et ajouté au fichier file d'attente consultable (prononcé "F-accessible"). La file d'attente consultable est une autre structure de données interne contrôlée par le ramasse-miettes . Chaque pointeur de la file d'attente identifiable identifie un objet qui est prêt à avoir sa méthode Finalize appelée.
Un thread d'exécution spécial est dédié à l'appel de Finalize méthodes. Lorsque la file d'attente consultable est vide (ce qui est généralement le cas ), Ce thread est en veille. Mais quand les entrées apparaissent, ce fil se réveille, supprime chaque entrée de la file d'attente et appelle Finalize .__ de chaque objet. méthode. Pour cette raison, vous ne devez exécuter aucun code dans un fichier Finalize méthode qui fait toute hypothèse sur le thread qui exécute le code. Par exemple, évitez d’accéder au stockage local du thread dans le fichier Finaliser la méthode. "
(*) À partir de novembre 2000, les choses ont peut-être changé depuis.
Lorsque les déchets sont collectés (en réponse à une pression de la mémoire ou à GC.Collect()
), les objets à finaliser sont placés dans la file d'attente de finalisation.
Sauf si vous appelez GC.WaitForPendingFinalizers()
, les finaliseurs peuvent continuer à s'exécuter en arrière-plan longtemps après la fin du nettoyage de la mémoire.
BTW, il n'y a aucune garantie que les finaliseurs s'appelleront du tout. De MSDN ...
La méthode Finalize risque de ne pas fonctionner complètement ou de ne pas fonctionner à le tout dans les circonstances exceptionnelles suivantes:
- Un autre finaliseur bloque indéfiniment (passe dans une boucle infinie, tente d'obtenir un verrou impossible à obtenir, etc.). Parce que le Le moteur d’exécution tente d’exécuter les finaliseurs jusqu’à achèvement, d’autres finaliseurs peut ne pas être appelé si un finaliseur bloque indéfiniment.
- Le processus se termine sans laisser au runtime l'occasion de nettoyer. Dans ce cas, la première notification du processus d'exécution par le runtime la terminaison est une notification DLL_PROCESS_DETACH.
Le moteur d'exécution continue de finaliser les objets lors de l'arrêt uniquement pendant que le nombre d'objets finalisables continue à diminuer.
Quelques points supplémentaires méritent d’être mentionnés ici.
Finalizer est le dernier point où les objets .net peuvent libérer des ressources non gérées. Les finaliseurs ne doivent être exécutés que si vous ne disposez pas correctement de vos instances. Idéalement, les finaliseurs ne devraient jamais être exécutés. Parce que la mise en œuvre appropriée doit supprimer la finalisation .
Voici un exemple pour une implémentation IDispoable correcte .
Si vous appelez la méthode Dispose de tout objet jetable, toutes les références doivent être effacées et la finalisation supprimée. S'il y a des développeurs pas très bons qui oublient d'appeler la méthode Dispose, Finalizer est la solution de sauvetage.