Je sais de la lecture de la documentation Microsoft que l'utilisation "principale" de l' IDisposable
interface est de nettoyer les ressources non gérées.
Pour moi, signifie « non gérés » des choses comme les connexions de base de données, prises, poignées de fenêtres, etc. Mais, j'ai le code vu où la Dispose()
méthode est mis en œuvre pour libérer gérées les ressources, ce qui me semble redondant, puisque le collecteur des ordures devrait prendre soin de c'est pour vous.
Par exemple:
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
Ma question est la suivante: cela fait-il que la mémoire du collecteur d'ordures est utilisée MyCollection
plus rapidement qu'elle ne le ferait normalement?
edit : Jusqu'à présent, les gens ont publié de bons exemples d'utilisation d'IDisposable pour nettoyer les ressources non gérées telles que les connexions aux bases de données et les bitmaps. Mais supposons que _theList
dans le code ci-dessus contienne un million de chaînes, et que vous vouliez libérer cette mémoire maintenant , plutôt que d'attendre le garbage collector. Le code ci-dessus accomplirait-il cela?
IDisposable
ne marque rien. LaDispose
méthode fait ce qu'elle doit faire pour nettoyer les ressources utilisées par l'instance. Cela n'a rien à voir avec GC.IDisposable
. Et c'est pourquoi j'ai dit que la réponse acceptée ne répond pas à la question prévue par le PO (et à la modification de suivi) quant à savoir si IDisposable aidera à <i> libérer de la mémoire </i>. Comme celaIDisposable
n'a rien à voir avec la libération de mémoire, seulement des ressources, alors comme vous l'avez dit, il n'est pas nécessaire de définir les références gérées sur null, ce que fait OP dans son exemple. Donc, la bonne réponse à sa question est "Non, cela n'aide pas à libérer de la mémoire plus rapidement. En fait, cela n'aide pas du tout à libérer de la mémoire, seulement des ressources". Mais de toute façon, merci pour votre contribution.Réponses:
Le but de Dispose est de libérer des ressources non managées. Cela doit être fait à un moment donné, sinon ils ne seront jamais nettoyés. Le garbage collector ne sait pas comment appeler
DeleteHandle()
à une variable de typeIntPtr
, il ne sait pas si oui ou non il doit appelerDeleteHandle()
.L'objet que vous avez créé doit exposer une méthode, que le monde extérieur peut appeler, afin de nettoyer les ressources non gérées. La méthode peut être nommée comme vous le souhaitez:
ou
Mais à la place, il existe un nom normalisé pour cette méthode:
Il y a même eu une interface créée
IDisposable
, qui n'a qu'une seule méthode:Donc, vous faites exposer votre objet à l'
IDisposable
interface, et de cette façon vous promettez que vous avez écrit cette seule méthode pour nettoyer vos ressources non managées:Et tu as fini. Sauf que vous pouvez faire mieux.
Que faire si votre objet a alloué un System.Drawing.Bitmap de 250 Mo (c'est-à-dire la classe Bitmap gérée .NET) comme une sorte de tampon de trame? Bien sûr, il s'agit d'un objet .NET géré et le garbage collector le libérera. Mais voulez-vous vraiment laisser 250 Mo de mémoire juste assis là - en attendant que le ramasse-miettes finisse par venir le libérer? Et s'il y a une connexion de base de données ouverte ? Certes, nous ne voulons pas que cette connexion reste ouverte, attendant que le GC finalise l'objet.
Si l'utilisateur a appelé
Dispose()
(ce qui signifie qu'il ne prévoit plus d'utiliser l'objet), pourquoi ne pas se débarrasser de ces bitmaps et connexions de base de données inutiles?Alors maintenant, nous allons:
Mettons donc à jour notre
Dispose()
méthode pour nous débarrasser de ces objets gérés:Et tout va bien, sauf que vous pouvez faire mieux !
Et si la personne a oublié d'appeler
Dispose()
votre objet? Ensuite, ils fuiraient des ressources non gérées !Si la personne a oublié d'appeler
Dispose()
, nous pouvons toujours conserver son bacon! Nous avons encore un moyen de l'appeler pour eux: quand le garbage collector se met enfin à libérer (c'est-à-dire finaliser) notre objet.La destruction de notre objet par le garbage collector est le moment idéal pour libérer ces satanées ressources non gérées. Nous le faisons en remplaçant la
Finalize()
méthode.Mais il y a un bug dans ce code. Vous voyez, le garbage collector s'exécute sur un thread d'arrière - plan ; vous ne connaissez pas l'ordre dans lequel deux objets sont détruits. Il est tout à fait possible que dans votre
Dispose()
code, l' objet géré dont vous essayez de vous débarrasser (parce que vous vouliez être utile) ne soit plus là:Donc, ce dont vous avez besoin est un moyen
Finalize()
de direDispose()
qu'il ne devrait toucher aucune ressource gérée (car ils pourraient ne plus y être ), tout en libérant des ressources non gérées.Le modèle standard pour ce faire est d'avoir
Finalize()
et lesDispose()
deux appellent une troisième (!) Méthode; où vous passez un dicton booléen si vous l'appelez depuisDispose()
(par opposition àFinalize()
), ce qui signifie qu'il est sûr de libérer des ressources gérées.Cette méthode interne peut recevoir un nom arbitraire comme "CoreDispose" ou "MyInternalDispose", mais il est de tradition de l'appeler
Dispose(Boolean)
:Mais un nom de paramètre plus utile pourrait être:
Et vous changez votre implémentation de la
IDisposable.Dispose()
méthode en:et votre finaliseur pour:
Et tout va bien, sauf que vous pouvez faire mieux !
Si l'utilisateur appelle
Dispose()
votre objet, alors tout a été nettoyé. Plus tard, lorsque le garbage collector arrive et appelle Finalize, il appelle àDispose
nouveau.Non seulement cela est un gaspillage, mais si votre objet a des références indésirables à des objets que vous avez déjà éliminés depuis le dernier appel
Dispose()
, vous essaierez de les éliminer à nouveau!Vous remarquerez dans mon code que j'ai pris soin de supprimer les références aux objets que j'ai supprimés, donc je n'essaie pas d'appeler
Dispose
une référence d'objet indésirable. Mais cela n'a pas empêché un bug subtil de s'introduire.Lorsque l'utilisateur appelle
Dispose()
: le handle CursorFileBitmapIconServiceHandle est détruit. Plus tard, lorsque le garbage collector s'exécute, il essaiera de détruire à nouveau la même poignée.La façon de résoudre ce problème est de dire au garbage collector qu'il n'a pas besoin de se soucier de finaliser l'objet - ses ressources ont déjà été nettoyées et aucun travail supplémentaire n'est nécessaire. Pour ce faire, appelez
GC.SuppressFinalize()
laDispose()
méthode:Maintenant que l'utilisateur a appelé
Dispose()
, nous avons:Il est inutile que le GC exécute le finaliseur - tout est réglé.
Ne puis-je pas utiliser Finalize pour nettoyer les ressources non gérées?
La documentation de
Object.Finalize
dit:Mais la documentation MSDN indique également, pour
IDisposable.Dispose
:Alors c'est quoi? Lequel est l'endroit pour moi de nettoyer les ressources non gérées? La réponse est:
Vous pouvez certainement placer votre nettoyage non géré dans le finaliseur:
Le problème est que vous n'avez aucune idée du moment où le garbage collector se mettra à finaliser votre objet. Vos ressources natives non gérées, non nécessaires et non utilisées resteront en place jusqu'à ce que le garbage collector s'exécute finalement . Ensuite, il appellera votre méthode de finalisation; nettoyer les ressources non gérées. La documentation d' Object.Finalize le souligne:
C'est la vertu de l'utilisation
Dispose
pour nettoyer les ressources non gérées; vous apprenez et contrôlez le nettoyage des ressources non gérées. Leur destruction est "déterministe" .Pour répondre à votre question initiale: pourquoi ne pas libérer de la mémoire maintenant plutôt que lorsque le GC décide de le faire? J'ai un logiciel de reconnaissance faciale besoins pour se débarrasser de 530 Mo d'images internes maintenant , car ils ne sont plus nécessaires. Quand nous ne le faisons pas: la machine s'arrête brusquement.
Lecture bonus
Pour tous ceux qui aiment le style de cette réponse (en expliquant le pourquoi , donc le comment devient évident), je vous suggère de lire le premier chapitre du COM essentiel de Don Box:
En 35 pages, il explique les problèmes d'utilisation des objets binaires et invente COM devant vos yeux. Une fois que vous comprenez le pourquoi de COM, les 300 pages restantes sont évidentes et détaillent simplement l'implémentation de Microsoft.
Je pense que tout programmeur qui a déjà traité d'objets ou de COM devrait, à tout le moins, lire le premier chapitre. C'est la meilleure explication de tout.
Lecture bonus supplémentaire
Quand tout ce que vous savez est faux par Eric Lippert
la source
null
. Tout d'abord, cela signifie que vous ne pouvez pas les fairereadonly
, et deuxièmement, vous devez faire des!=null
vérifications très laides (comme dans l'exemple de code). Vous pourriez avoir un drapeaudisposed
, mais il est plus facile de ne pas s'en soucier. Le GC .NET est suffisamment agressif pour qu'une référence à un champx
ne soit plus considérée comme «utilisée» au moment où elle passe lax.Dispose()
ligne.IDisposable
est souvent utilisé pour exploiter l'using
instruction et profiter d'un moyen simple de faire un nettoyage déterministe des objets gérés.la source
Le modèle Dispose a pour objet de fournir un mécanisme de nettoyage des ressources gérées et non gérées et le moment où cela se produit dépend de la façon dont la méthode Dispose est appelée. Dans votre exemple, l'utilisation de Dispose ne fait en réalité rien de ce qui est lié à l'élimination, car l'effacement d'une liste n'a aucun impact sur la suppression de cette collection. De même, les appels pour définir les variables sur null n'ont également aucun impact sur le GC.
Vous pouvez consulter cet article pour plus de détails sur la façon d'implémenter le modèle Dispose, mais il ressemble essentiellement à ceci:
La méthode la plus importante ici est le Dispose (bool), qui s'exécute en fait dans deux circonstances différentes:
Le simple fait de laisser le GC se charger du nettoyage est que vous n'avez aucun contrôle réel sur le moment où le GC exécutera un cycle de collecte (vous pouvez appeler GC.Collect (), mais vous ne devriez vraiment pas) pour que les ressources restent plus longtemps que nécessaire. N'oubliez pas que l'appel à Dispose () ne provoque pas réellement de cycle de collecte ni n'entraîne en aucune façon le GC à collecter / libérer l'objet; il fournit simplement les moyens de nettoyer de manière plus déterministe les ressources utilisées et indique au GC que ce nettoyage a déjà été effectué.
L'intérêt d'IDisposable et du modèle d'élimination ne consiste pas à libérer immédiatement la mémoire. La seule fois où un appel à Dispose aura même une chance de libérer immédiatement de la mémoire, c'est quand il gère le faux scénario d'élimination == et manipule des ressources non gérées. Pour le code managé, la mémoire ne sera pas réellement récupérée jusqu'à ce que le GC exécute un cycle de collecte, sur lequel vous n'avez vraiment aucun contrôle (autre que d'appeler GC.Collect (), que j'ai déjà mentionné n'est pas une bonne idée).
Votre scénario n'est pas vraiment valide, car les chaînes dans .NET n'utilisent aucune ressource non modifiée et n'implémentent pas IDisposable, il n'y a aucun moyen de les forcer à être «nettoyées».
la source
Il ne doit plus y avoir d'appels aux méthodes d'un objet après l'appel de Dispose (bien qu'un objet doive tolérer d'autres appels à Dispose). Par conséquent, l'exemple de la question est stupide. Si Dispose est appelé, l'objet lui-même peut être rejeté. Ainsi, l'utilisateur doit simplement supprimer toutes les références à cet objet entier (les définir sur null) et tous les objets associés internes à celui-ci seront automatiquement nettoyés.
En ce qui concerne la question générale sur les ressources gérées / non gérées et la discussion dans d'autres réponses, je pense que toute réponse à cette question doit commencer par une définition d'une ressource non gérée.
Cela revient à dire qu'il y a une fonction que vous pouvez appeler pour mettre le système dans un état, et il y a une autre fonction que vous pouvez appeler pour le faire sortir de cet état. Maintenant, dans l'exemple typique, la première peut être une fonction qui renvoie un descripteur de fichier et la seconde peut être un appel à
CloseHandle
.Mais - et c'est la clé - il pourrait s'agir de n'importe quelle paire de fonctions correspondante. L'un construit un État, l'autre le démolit. Si l'état a été construit mais pas encore détruit, alors une instance de la ressource existe. Vous devez organiser le démontage au bon moment - la ressource n'est pas gérée par le CLR. Le seul type de ressource géré automatiquement est la mémoire. Il existe deux types: le GC et la pile. Les types de valeur sont gérés par la pile (ou en attelant un trajet à l'intérieur des types de référence), et les types de référence sont gérés par le GC.
Ces fonctions peuvent provoquer des changements d'état qui peuvent être librement entrelacés ou peuvent devoir être parfaitement imbriqués. Les changements d'état peuvent être threadsafe ou non.
Regardez l'exemple de la question de Justice. Les modifications apportées à l'indentation du fichier journal doivent être parfaitement imbriquées, sinon tout va mal. Il est également peu probable qu'ils soient threadsafe.
Il est possible de faire du stop avec le ramasse-miettes pour nettoyer vos ressources non gérées. Mais seulement si les fonctions de changement d'état sont threadsafe et que deux états peuvent avoir des durées de vie qui se chevauchent de quelque manière que ce soit. Ainsi, l'exemple d'une ressource par Justice ne doit PAS avoir de finaliseur! Cela n'aiderait personne.
Pour ces types de ressources, vous pouvez simplement implémenter
IDisposable
, sans finaliseur. Le finaliseur est absolument facultatif - il doit l'être. Ceci est passé sous silence ou même pas mentionné dans de nombreux livres.Vous devez ensuite utiliser la
using
déclaration pour avoir la moindre chance de vous assurer qu'elleDispose
est appelée. C'est essentiellement comme atteler un tour avec la pile (de sorte que le finaliseur est au GC,using
est à la pile).La partie manquante est que vous devez écrire manuellement Dispose et le faire appeler dans vos champs et votre classe de base. Les programmeurs C ++ / CLI n'ont pas à le faire. Le compilateur l'écrit pour eux dans la plupart des cas.
Il existe une alternative, que je préfère pour les états qui s'imbriquent parfaitement et ne sont pas threadsafe (en dehors de toute autre chose, éviter IDisposable vous évite d'avoir un argument avec quelqu'un qui ne peut pas résister à l'ajout d'un finaliseur à chaque classe qui implémente IDisposable) .
Au lieu d'écrire une classe, vous écrivez une fonction. La fonction accepte un délégué pour rappeler à:
Et puis un exemple simple serait:
Le lambda transmis sert de bloc de code, c'est donc comme si vous créez votre propre structure de contrôle pour servir le même but que
using
, sauf que vous n'avez plus aucun danger que l'appelant en abuse. Il n'y a aucun moyen qu'ils ne parviennent pas à nettoyer la ressource.Cette technique est moins utile si la ressource est du type qui peut avoir des durées de vie qui se chevauchent, car alors vous voulez pouvoir créer la ressource A, puis la ressource B, puis tuer la ressource A et ensuite tuer la ressource B. Vous ne pouvez pas le faire si vous avez forcé l'utilisateur à s'emboîter parfaitement comme ça. Mais alors vous devez utiliser
IDisposable
(mais toujours sans finaliseur, sauf si vous avez implémenté threadsafety, qui n'est pas gratuit).la source
enter
etexit
est au cœur de la façon dont je pense d'une ressource. L'inscription / la désinscription à des événements doit s'intégrer sans difficulté. En termes de caractéristiques orthogonales / fongibles, il est pratiquement impossible de les distinguer d'une fuite de mémoire. (Ce n'est pas surprenant car l'abonnement ajoute simplement des objets à une liste.)Scénarios que j'utilise IDisposable: nettoyer les ressources non managées, se désinscrire des événements, fermer les connexions
L'idiome que j'utilise pour implémenter IDisposable ( pas threadsafe ):
la source
Oui, ce code est complètement redondant et inutile et il ne fait pas faire au garbage collector tout ce qu'il ne ferait pas autrement (une fois qu'une instance de MyCollection est hors de portée, c'est-à-dire.) Surtout les
.Clear()
appels.Réponse à votre modification: en quelque sorte. Si je fais ça:
Il est fonctionnellement identique à celui-ci à des fins de gestion de la mémoire:
Si vous avez vraiment vraiment vraiment besoin de libérer la mémoire à l'instant même, appelez
GC.Collect()
. Il n'y a cependant aucune raison de le faire ici. La mémoire sera libérée en cas de besoin.la source
Si des
MyCollection
déchets doivent être récupérés de toute façon, vous ne devriez pas avoir à les jeter. Cela ne fera que déstabiliser le CPU plus que nécessaire, et peut même invalider une analyse pré-calculée que le garbage collector a déjà effectuée.J'utilise
IDisposable
pour faire des choses comme s'assurer que les threads sont éliminés correctement, ainsi que les ressources non gérées.EDIT En réponse au commentaire de Scott:
Sur le plan conceptuel, le GC conserve une vue du graphique de référence de l'objet et de toutes les références à celui-ci à partir des cadres de pile des threads. Ce tas peut être assez volumineux et s'étendre sur plusieurs pages de mémoire. À titre d'optimisation, le GC met en cache son analyse des pages qui ne sont pas susceptibles de changer très souvent pour éviter une nouvelle numérisation inutile de la page. Le GC reçoit une notification du noyau lorsque les données d'une page changent, il sait donc que la page est sale et nécessite une nouvelle analyse. Si la collection est dans Gen0, il est probable que d'autres éléments de la page changent également, mais cela est moins probable dans Gen1 et Gen2. Pour l'anecdote, ces crochets n'étaient pas disponibles dans Mac OS X pour l'équipe qui a porté le GC sur Mac afin de faire fonctionner le plug-in Silverlight sur cette plate-forme.
Autre point contre l'élimination inutile des ressources: imaginez une situation où un processus se décharge. Imaginez également que le processus fonctionne depuis un certain temps. Il est probable que la plupart des pages de mémoire de ce processus ont été échangées sur le disque. À tout le moins, ils ne sont plus dans le cache L1 ou L2. Dans une telle situation, il n'y a aucun intérêt pour une application en cours de déchargement à échanger toutes ces données et pages de codes en mémoire pour `` libérer '' les ressources qui seront de toute façon libérées par le système d'exploitation à la fin du processus. Cela s'applique aux ressources gérées et même à certaines ressources non gérées. Seules les ressources qui maintiennent des threads non-fond actifs doivent être supprimées, sinon le processus restera actif.
Maintenant, pendant l'exécution normale, il existe des ressources éphémères qui doivent être nettoyées correctement (comme @fezmonkey souligne les connexions à la base de données, les sockets, les poignées de fenêtre ) pour éviter les fuites de mémoire non gérées. Voilà le genre de choses qui doivent être éliminées. Si vous créez une classe qui possède un thread (et par propriétaire, je veux dire qu'il l'a créé et qu'il est donc responsable de s'assurer qu'il s'arrête, au moins par mon style de codage), alors cette classe doit très probablement implémenter
IDisposable
et supprimer le thread pendantDispose
.Le framework .NET utilise l'
IDisposable
interface comme un signal, voire un avertissement, aux développeurs que la classe this doit être supprimée. Je ne peux penser à aucun type dans le cadre qui implémenteIDisposable
(à l'exclusion des implémentations d'interface explicite) où l'élimination est facultative.la source
Dispose()
appels facultatifs , voir: stackoverflow.com/questions/913228/…Dans l'exemple que vous avez publié, il ne "libère pas la mémoire maintenant". Toute la mémoire est récupérée, mais elle peut permettre à la mémoire d'être collectée dans une génération antérieure . Il faudrait exécuter des tests pour en être sûr.
Les directives de conception du cadre sont des lignes directrices et non des règles. Ils vous indiquent à quoi sert principalement l'interface, quand l'utiliser, comment l'utiliser et quand ne pas l'utiliser.
J'ai lu une fois du code qui était un simple RollBack () en cas d'échec en utilisant IDisposable. La classe MiniTx ci-dessous vérifierait un indicateur sur Dispose () et si l'
Commit
appel ne se produisait jamais, il s'appellerait alorsRollback
sur lui-même. Il a ajouté une couche d'indirection rendant le code appelant beaucoup plus facile à comprendre et à maintenir. Le résultat ressemblait à quelque chose comme:J'ai également vu le code de synchronisation / journalisation faire la même chose. Dans ce cas, la méthode Dispose () a arrêté le minuteur et enregistré que le bloc était sorti.
Voici donc quelques exemples concrets qui n'effectuent aucun nettoyage de ressources non gérées, mais utilisent avec succès IDisposable pour créer un code plus propre.
la source
Si vous souhaitez supprimer maintenant , utilisez la mémoire non gérée .
Voir:
la source
Je ne répéterai pas les choses habituelles sur l'utilisation ou la libération de ressources non gérées, qui ont toutes été couvertes. Mais je voudrais souligner ce qui semble être une idée fausse commune.
Étant donné le code suivant
Je me rends compte que la mise en œuvre jetable ne suit pas les directives actuelles, mais j'espère que vous avez tous compris l'idée.
Maintenant, lorsque Dispose est appelé, combien de mémoire est libérée?
Réponse: aucune.
Calling Dispose peut libérer des ressources non managées, il NE PEUT PAS récupérer la mémoire gérée, seul le GC peut le faire. Cela ne veut pas dire que ce qui précède n'est pas une bonne idée, suivre le modèle ci-dessus est toujours une bonne idée en fait. Une fois Dispose exécuté, rien n'empêche le GC de revendiquer à nouveau la mémoire utilisée par _Large, même si l'instance de LargeStuff peut toujours être à portée. Les chaînes de _Large peuvent également être de génération 0, mais l'instance de LargeStuff peut être de génération 2, donc encore une fois, la mémoire sera revendiquée plus tôt.
Il est inutile d'ajouter un finaliseur pour appeler la méthode Dispose illustrée ci-dessus. Cela retardera simplement le renouvellement de la mémoire pour permettre au finaliseur de fonctionner.
la source
LargeStuff
existe depuis assez longtemps pour atteindre la génération 2 et si elle_Large
contient une référence à une chaîne nouvellement créée qui se trouve dans la génération 0, alors si l'instance deLargeStuff
est abandonnée sans annulation_Large
, la chaîne référencée par_Large
sera conservé jusqu'à la prochaine collection Gen2. La remise à zéro_Large
peut laisser la chaîne être éliminée lors de la prochaine collection Gen0. Dans la plupart des cas, l'annulation des références n'est pas utile, mais il peut y avoir des avantages dans certains cas.Outre son utilisation principale comme moyen de contrôler la durée de vie des ressources système (entièrement couvert par la réponse impressionnante de Ian , bravo!), Le combo IDisposable / using peut également être utilisé pour définir le changement d'état des ressources globales (critiques) : la console , les threads , le processus , tout objet global comme une instance d'application .
J'ai écrit un article sur ce modèle: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/
Il illustre comment vous pouvez protéger certains états globaux souvent utilisés de manière réutilisable et lisible : couleurs de la console , culture actuelle des threads , propriétés des objets d'application Excel ...
la source
Si quoi que ce soit, je m'attendrais à ce que le code soit moins efficace qu'en le laissant de côté.
L'appel des méthodes Clear () n'est pas nécessaire, et le GC ne le ferait probablement pas si le Dispose ne le faisait pas ...
la source
Il y a des choses que l'
Dispose()
opération fait dans l'exemple de code qui pourraient avoir un effet qui ne se produirait pas en raison d'un GC normal de l'MyCollection
objet.Si les objets référencés par
_theList
ou_theDict
sont référencés par d'autres objets, alors cet objetList<>
ou celui -Dictionary<>
ci ne sera pas soumis à collection mais n'aura soudainement aucun contenu. S'il n'y avait pas d'opération Dispose () comme dans l'exemple, ces collections contiendraient toujours leur contenu.Bien sûr, si telle était la situation, je l'appellerais une conception cassée - je souligne simplement (d'un point de vue pédant, je suppose) que l'
Dispose()
opération pourrait ne pas être complètement redondante, selon qu'il existe d'autres utilisations duList<>
ouDictionary<>
qui ne le sont pas. montré dans le fragment.la source
Un problème avec la plupart des discussions sur les «ressources non managées» est qu'elles ne définissent pas vraiment le terme, mais semblent impliquer qu'il a quelque chose à voir avec le code non managé. S'il est vrai que de nombreux types de ressources non managées interfacent avec du code non managé, il n'est pas utile de penser aux ressources non managées en de tels termes.
Au lieu de cela, il faut reconnaître ce que toutes les ressources gérées ont en commun: elles impliquent toutes un objet demandant à une `` chose '' extérieure de faire quelque chose en son nom, au détriment d'autres `` choses '', et l'autre entité acceptant de le faire jusqu'à ce que nouvel avis. Si l'objet devait être abandonné et disparaître sans laisser de trace, rien ne dirait jamais à cette «chose» extérieure qu'elle n'avait plus besoin de modifier son comportement au nom de l'objet qui n'existait plus; par conséquent, l'utilité de la chose serait définitivement diminuée.
Une ressource non gérée, alors, représente un accord par une `` chose '' extérieure pour modifier son comportement au nom d'un objet, ce qui nuirait inutilement à l'utilité de cette `` chose '' extérieure si l'objet était abandonné et cessait d'exister. Une ressource gérée est un objet qui bénéficie d'un tel accord, mais qui s'est inscrit pour recevoir une notification en cas d'abandon et qui utilisera cette notification pour mettre de l'ordre dans ses affaires avant sa destruction.
la source
IDisposable
est bon pour se désinscrire d'événements.la source
Premier de définition. Pour moi, une ressource non gérée signifie une classe, qui implémente une interface IDisposable ou quelque chose créé avec l'utilisation d'appels à la DLL. GC ne sait pas comment gérer de tels objets. Si la classe n'a par exemple que des types de valeur, alors je ne considère pas cette classe comme une classe avec des ressources non managées. Pour mon code, je suis les pratiques suivantes:
suivant montre ce que j'ai décrit en mots comme exemple de code:
la source
is IDisposable
devrait lui-même être considéré comme une ressource non gérée? Cela ne semble pas correct. De plus, si le type d'implémentation est un type de valeur pure, vous semblez suggérer qu'il n'a pas besoin d'être supprimé. Cela semble également faux.Votre exemple de code donné n'est pas un bon exemple d'
IDisposable
utilisation. Normalement, l' effacement du dictionnaire ne doit pas aller à laDispose
méthode. Les éléments du dictionnaire seront effacés et supprimés lorsqu'ils seront hors de portée.IDisposable
l'implémentation est requise pour libérer de la mémoire / des gestionnaires qui ne seront pas libérés / libérés même après qu'ils soient hors de portée.L'exemple suivant montre un bon exemple de modèle IDisposable avec du code et des commentaires.
la source
Le cas d'utilisation le plus justifié pour l'élimination des ressources gérées est la préparation du GC à récupérer des ressources qui autrement ne seraient jamais collectées.
Un bon exemple est les références circulaires.
Bien qu'il soit préférable d'utiliser des modèles qui évitent les références circulaires, si vous vous retrouvez avec (par exemple) un objet `` enfant '' qui a une référence à son `` parent '', cela peut arrêter la collecte GC du parent si vous abandonnez simplement la référence et compter sur GC - plus si vous avez implémenté un finaliseur, il ne sera jamais appelé.
La seule façon de contourner ce problème consiste à rompre manuellement les références circulaires en définissant les références parent à null sur les enfants.
Implémenter IDisposable sur les parents et les enfants est le meilleur moyen de le faire. Lorsque Dispose est appelé sur le parent, appelez Dispose sur tous les enfants et dans la méthode enfant Dispose, définissez les références parent sur null.
la source
WeakReference
, le système vérifie un indicateur qui indique qu'une référence enracinée en direct a été trouvée dans le dernier cycle GC et ajoutera l'objet à une file d'attente d'objets nécessitant une finalisation immédiate, libérera l'objet du tas d'objets volumineux ou invalidera la référence faible. Les références circulaires ne garderont pas les objets en vie s'il n'existe aucune autre référence.Je vois que beaucoup de réponses se sont déplacées pour parler de l'utilisation d'IDisposable pour les ressources gérées et non gérées. Je suggère cet article comme l'une des meilleures explications que j'ai trouvées sur la façon dont IDisposable doit être utilisé.
https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About
Pour la question réelle; si vous utilisez IDisposable pour nettoyer les objets gérés qui prennent beaucoup de mémoire, la réponse courte serait non . La raison en est qu'une fois que vous vous débarrassez d'un IDisposable, vous devez le laisser hors de portée. À ce stade, tous les objets enfants référencés sont également hors de portée et seront collectés.
La seule véritable exception à cela serait si vous avez beaucoup de mémoire liée aux objets gérés et que vous avez bloqué ce thread en attendant qu'une opération soit terminée. Si ces objets ne sont plus nécessaires une fois cet appel terminé, la définition de ces références sur null peut permettre au garbage collector de les collecter plus tôt. Mais ce scénario représenterait un mauvais code qui devait être refactorisé - pas un cas d'utilisation d'IDisposable.
la source