Comment la récupération de place fonctionne-t-elle sur les références d’object?

Je suis confus à propos du processus de récupération de place sur des objects.

object A = new object(); object B = A; B.Dispose(); 

En appelant un object Dispose sur la variable B uniquement, l’object créé ne sera pas nettoyé car l’object a toujours été référencé par A.

Maintenant le code suivant fonctionne-t-il comme ci-dessus?

 public static image Test1() { Bitmap A = new Bitmap(); return A; } 

Maintenant, j’appelle cette fonction statique à partir d’une autre méthode.

 public void TestB() { Bitmap B = Test1(); B.Dispose(); } 

La fonction statique Test1 a renvoyé une référence à l’object Bitmap. La référence est enregistrée dans une autre variable B. En appelant un Dispose on B, la connexion entre B et l’object est perdue, mais il en va de même pour la référence transmise à partir de Test1. Restera-t-il actif jusqu’à la fin de la scope de la fonction TestB?

Existe-t-il un moyen de supprimer immédiatement la référence transmise par la fonction statique?

Je suis peut-être en congé, mais vous semblez avoir mal compris l’ Dispose et le ramassage des ordures. Un object sera récupéré une fois que toutes les références à celui-ci auront disparu, de manière non déterministe. L’élimination supprime généralement les ressources non gérées, de sorte que l’object est prêt à être récupéré. Dans votre premier exemple, vous avez jeté l’object, le rendant théoriquement inutilisable, mais il existe toujours sur le tas et vous y avez toujours une référence, à la fois A et B. Une fois que ceux-ci sont hors de scope, le ramasse-miettes peut récupérer cette mémoire. , mais pas toujours. Dans l’exemple 2, un bitmap A est placé sur le tas, vous en renvoyez une référence et définissez B sur cette référence. Ensuite, vous en disposez et B sort du cadre. À ce stade, il n’existe plus de références à ce sujet et il sera collecté ultérieurement.

Dispose ne pas ramasser les ordures. Vous ne pouvez pas explicitement ramasser un object en particulier. Vous pouvez appeler GC.Collect() demander à ce que le ramasse-miettes s’exécute, mais ce n’est pas la même chose. L’appel de Dispose ne “déconnecte” même pas l’object d’une variable particulière, en fait … tant que cette variable rest active (jusqu’à la dernière fois que le JIT peut détecter qu’il sera lu à nouveau), il empêchera l’object de être des ordures collectées.

Un object ne sera pas ramassé jusqu’à ce qu’il ne soit plus référencé par rien. Certes, cela peut être plus tôt que vous ne le pensez dans certains cas extrêmes, mais vous devez rarement vous en préoccuper.

Il faut savoir que l’ Dispose et le ramassage des ordures sont des choses très différentes. Vous appelez Dispose pour libérer des ressources non gérées (connexions réseau, etc.). Le ramassage des ordures sert uniquement à libérer de la mémoire. Certes, la récupération de place peut être finalisée, ce qui peut libérer des ressources non gérées en dernier recours, mais la plupart du temps, vous devez vous débarrasser explicitement des ressources non gérées.

Il se trouve que Raymond Chen vient d’écrire une série d’articles de blogs décrivant certains aspects de la collecte des ordures .NET. Ce message concerne le plus directement votre question (à quel moment les objects sont-ils ramassés?).

Dispose () n’a rien à voir avec la récupération de place. Tout ce que cela fait est de permettre une libération déterministe des ressources, mais vous devez l’appeler explicitement. L’object sur lequel vous l’appelez ne sera pas récupéré lorsque vous appelez Dispose (). Il sera éligible pour le ramassage des ordures quand toutes les références à celle-ci auront disparu.

Beaucoup de bonnes réponses ici mais j’aime aussi souligner que la raison pour laquelle les gens pensaient que vous aviez besoin d’IDisposable est qu’un GC doit en réalité s’appeler MemoryCollector ou même ManagedMemoryCollector. Un GC n’est pas particulièrement intelligent lorsqu’il s’agit de collecter des ressources de mémoire non gérées telles que des fichiers, des bases de données, des transactions, des descripteurs Windows, etc.

Une des raisons est qu’un object géré peut avoir une ressource non gérée qui prend plusieurs Go de RAM, mais pour le GC, cela ressemble à environ 8 octets.

Avec les fichiers, les bases de données, etc., vous souhaitez souvent les fermer le plus tôt possible pour libérer des ressources non gérées et éviter les problèmes de locking.

Avec les poignées Windows, nous avons une affinité de fil à craindre. Lorsqu’un CPG s’exécute dans un thread dédié, ce thread est toujours le mauvais thread pour libérer les handles de Windows.

Donc, GC aide beaucoup à éviter les memory leaks gérée et à réduire l’encombrement du code, mais il faut tout de même libérer les ressources non gérées dès que possible.

using () est une bénédiction.

PS Bien souvent, j’implémente IDisposable même si je n’ai aucune ressource directe non gérée, mais son importation a pour but d’informer toutes les variables membres implémentant IDisposable que Dispose a été appelé.

Ok pour les débutants Dispose! = Garbage Collected. Vous pouvez appeler dispose et ne jamais le faire ramasser, car un “object jeté” peut toujours avoir des références. La méthode dispose est utilisée pour “ranger” l’object avant que le CG ne s’exécute (connexions ouvertes à la firebase database, connexions au fichier, etc.).

 object A = new object(); object B = A; B.Dispose(); 

Dans ce cas, B.Dispose appelle la méthode dispose sur A, car B fait référence à l’object dans la variable A. Ni l’un ni l’autre ne seront CGd, car ils ne sont pas encore sortis de la scope.

 public static image Test1() { Bitmap A = new Bitmap(); return A; } 

Ce qui se passe ici, c’est que vous créez et restituez l’object A; ainsi, lorsque vous quittez Test1, il est fort probable que A soit référencé par une autre variable dans la méthode d’appel. Cela signifie que même si vous avez quitté la méthode, A est toujours enraciné (probablement) et ne sera pas généré en CG tant que la méthode d’appel n’en aura pas terminé.

 public void TestB() { Bitmap B = Test1(); B.Dispose(); } 

Ici, B est créé et appelle dispose. Cela ne signifie pas qu’il sera gargable collecté. Une fois que le programme quitte la méthode, B sort du champ d’application, ce qui signifie qu’il peut être collecté lors du prochain appel du GC.

Quand utiliser Dispose

Il peut être intéressant de noter que l’appel de Dispose peut en fait ne rien faire du tout. Cela donne à l’object une chance de nettoyer des ressources telles que des connexions à une firebase database et des ressources non gérées. Si vous avez un object contenant une ressource non gérée, telle qu’une connexion à une firebase database, Dispose lui dira qu’il est temps de nettoyer ces références.

La question fondamentale de la récupération de place est: “Cet object peut-il être atteint?” Tant qu’il y a un object sur la stack qui a une référence à votre object (ou une référence à cet object quelque part dans la hiérarchie des objects), l’object ne sera pas nettoyé.

Exemple:

ObjA crée un ObjB, qui crée un ObjC. Obj C ne sera pas récupéré tant qu’il n’aura plus été référencé par ObjB ou qu’objB ne sera plus référencé par ObjA, ou tant qu’aucun object ne conservera une référence à ObjA.

Là encore, la question à poser est la suivante: “Cet object peut-il actuellement être référencé par quelque chose dans le code?”