La bonne utilisation de l'interface IDisposable
Je sais de lecture la documentation de Microsoft que le "primaire" de l'utilisation de la IDisposable
interface est de nettoyer les ressources non managées.
Pour moi, "non géré" signifie des choses comme des connexions de base de données, des sockets, des poignées de fenêtre, etc. Mais, j'ai vu du code où l' Dispose()
méthode est mise en œuvre à la libre géré ressources, qui semble redondant pour moi, depuis le garbage collector devrait prendre soin de cela 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, est-ce faire le garbage collector libérer la mémoire utilisée par MyCollection
plus vite qu'il le ferait normalement?
modifier: Donc, beaucoup de gens ont posté quelques bons exemples de l'utilisation de IDisposable pour nettoyer les ressources non managées comme des connexions de base de données et d'images. Mais supposons que _theList
dans le code ci-dessus figurant un million de chaînes, et que vous vouliez pour libérer de la mémoire maintenant, plutôt que d'attendre que le garbage collector. Serait le code ci-dessus le réaliser?
- J'aime la accepté de répondre parce que vous dire la bonne "pattern" de l'aide IDisposable, mais comme l'OP a dit, dans son édition, il ne répond pas à sa destinée question. IDisposable ne fait pas appel de la GC, c'est juste les "marques" à un objet destructible. Mais quelle est la véritable façon de libérer de la mémoire "right now" au lieu d'attendre pour les GC à coup de pied? Je pense que cette question mérite un examen plus approfondi.
IDisposable
ne marque pas de quoi que ce soit. LeDispose
méthode fait ce qu'il a à faire pour nettoyer les ressources utilisées par l'instance. Cela n'a rien à voir avec le GC.- Je pense que vous avez mal compris ma question. Assumer ma classe implémente IDisposable et dans ma classe, je suis en utilisant une très grande liste de tableaux ou de quelque autre objet géré que j'ai mis à l'(marque comme) null lorsque l'Éliminer(true) est appelée sur une instance de ma classe. Maintenant, selon le modèle, ce que je fais ici est le marquage de la liste de tableaux de référence à null, mais la liste de tableaux d'objets de données dans la mémoire jusqu'à ce que le GC coups de pied dans ce qui est non-déterministe. Donc, la question est de savoir comment gratuit ce non référencées bloc de mémoire immédiatement et de façon déterministe.
- encore une fois, vous êtes malentendu
IDisposable
. Il n'a rien à voir avec la libération de la mémoire. En outre, vous ne devez pas mettre votre référence à la valeur null à tous. Le laisser seul et de laisser le GC faire son travail. - Je ne comprends
IDisposable
. Et c'est pourquoi j'ai dit que l'on a accepté de répondre à ne pas répondre à l'OP est prévu à la question(et le suivi edit) si IDisposable aidera dans <i>la libération de la mémoire</i>. DepuisIDisposable
n'a rien à voir avec la libération de la mémoire, de ressources, alors comme vous l'avez dit, il n'est pas nécessaire de définir la gestion des références à null, qui est ce que l'OP a été fait dans son exemple. Ainsi, la bonne réponse à sa question est "Non, ce n'est pas les aider à libérer de la mémoire plus rapide. En fait, il n'a pas d'aider à libérer de la mémoire, mais seulement de ressources". Mais de toute façon, merci pour vos commentaires. - si c'est le cas, alors vous ne devriez pas avoir dit "IDisposable ne fait pas appel de la GC, c'est juste les "marques" à un objet destructible"
- Il n'y a aucun moyen de garantie de libérer de la mémoire de façon déterministe. Vous pourriez l'appeler GC.Collect(), mais c'est une demande polie, pas une demande. Tous les threads en cours d'exécution doit être suspendu pour la collecte des ordures à procéder - lire sur le concept de .NET points de restauration si vous voulez en savoir plus, par exemple, msdn.microsoft.com/en-us/library/678ysw69(v=vs. 110).aspx . Si un thread ne peut pas être suspendu, par exemple, parce qu'il y a un appel dans le code non managé, GC.Collect() peut ne rien faire du tout.
Vous devez vous connecter pour publier un commentaire.
Le point de Jeter est pour libérer des ressources non managées. Il doit être fait à un certain point, sinon ils ne seront jamais nettoyé. Le garbage collector ne sais pas comment appeler
DeleteHandle()
sur une variable de typeIntPtr
, il ne sait pas si ou non il a besoin de faire appelDeleteHandle()
.L'objet que vous avez créé doit exposer certains méthode, que le monde extérieur peut faire appel, afin de nettoyer les ressources non managées. La méthode peut être le nom que vous voulez:
ou
Mais il y a un nom normalisé pour cette méthode:
Il y a même une interface créée,
IDisposable
, qui a une seule méthode:Afin de vous rendre votre objet d'exposer la
IDisposable
interface, et de cette façon vous promets que vous avez écrit que la seule méthode pour nettoyer vos ressources non managées:Et vous avez terminé. Sauf que vous pouvez faire mieux.
Que si votre objet a alloué une 250MB Système.De dessin.Bitmap (c'est à dire l' .NET géré classe Bitmap) comme une sorte de mémoire tampon de trame? Bien sûr, cela est géré .NET de l'objet, et le garbage collector gratuit. Mais voulez-vous vraiment quitter 250 MO de mémoire juste assis là en attente pour le garbage collector de finalement venir et de les libérer? Que faire si il y a un ouvrir la connexion à la base? Certes, nous ne voulons pas que la connexion séance ouverte, en attente pour le GC pour finaliser l'objet.
Si l'utilisateur a appelé
Dispose()
(ce qui signifie qu'ils ne sont plus du plan d'utilisation de l'objet), pourquoi ne pas se débarrasser de ceux gaspillage des bitmaps et des connexions de base de données?Alors maintenant, nous allons:
Donc, nous allons mettre à jour notre
Dispose()
méthode pour se débarrasser de ces objets gérés:Et c'est tout bon, sauf que vous pouvez faire mieux!
Que faire si la personne oublié appeler
Dispose()
sur votre objet? Alors qu'ils seraient à la fuite de certains non géré de ressources!Si la personne a oublié d'appeler
Dispose()
, nous pouvons encore sauver leur bacon! Nous avons encore un moyen de l'appeler pour: lorsque le garbage collector, enfin, se déplace à libérer (c'est à dire la finalisation) de notre objet.La destruction de notre objet par le Garbage collector est le parfait temps de libre ces satanés des ressources non managées. Nous faisons cela en remplaçant la
Finalize()
méthode.Mais il y a un bug dans le code. Vous voyez, le garbage collector s'exécute sur un thread d'arrière-plan; vous ne savez pas l'ordre dans lequel les deux objets sont détruits. Il est tout à fait possible que, dans votre
Dispose()
code, le géré objet que vous essayez de vous débarrasser de (parce que tu voulais être utile) n'est plus: il y aDonc, ce que vous avez besoin est une façon pour
Finalize()
direDispose()
qu'il devrait touchez pas réussi ressources (parce qu'ils pourrait ne pas être là plus), tout en libérant des ressources non managées.Au modèle standard pour ce faire est d'avoir
Finalize()
etDispose()
la fois d'un troisième(!) méthode; où l'on passe un Booléen disant: si vous appelez à partir deDispose()
(par opposition àFinalize()
), ce qui signifie qu'il est sûr de gratuit ressources gérées.Ce interne méthode pourrait être donné quelques arbitraire nom comme "CoreDispose", ou "MyInternalDispose", mais c'est la tradition de l'appeler
Dispose(Boolean)
:Mais plus utile de nom de paramètre peut être:
Et vous changez la mise en œuvre de la
IDisposable.Dispose()
méthode:et votre finaliseur à:
Et c'est tout bon, sauf que vous pouvez faire mieux!
Si l'utilisateur appelle
Dispose()
sur votre objet, puis tout a été nettoyé. Plus tard, lorsque le garbage collector vient le long et les appels de Finaliser, il va alors appelerDispose
de nouveau.Ce n'est pas seulement inutile, mais si votre objet a ordure des références à des objets que vous avez déjà éliminés de la dernier appel à
Dispose()
, que vous allez essayer de les disposer à nouveau!Vous remarquerez que dans mon code j'ai eu soin de supprimer les références à des objets que j'ai cédé, je n'ai pas essayer d'appeler
Dispose
à bord d'une jonque de référence de l'objet. Mais cela n'empêche pas un bug subtil rampant dans.Lorsque l'utilisateur appelle
Dispose()
: la poignée CursorFileBitmapIconServiceHandle est détruit. Plus tard, lorsque le garbage collector est exécuté, il va tenter de détruire le même handle de nouveau.La façon de résoudre ce problème est de dire que le garbage collector qu'il n'a pas besoin de s'embêter à la finalisation de l'objet de ses ressources ont déjà été nettoyés, et pas plus de travail est nécessaire. Vous faites cela en appelant
GC.SuppressFinalize()
dans leDispose()
méthode:Maintenant que l'utilisateur a appelé
Dispose()
, nous avons:Il n'y a pas de point dans le GC de l'exécution de l'outil de finalisation, tout est pris en charge.
Pourrais-je ne pas utiliser Finaliser pour le nettoyage de ressources non managées?
La documentation pour
de l'Objet.Finaliser
dit:Mais la documentation MSDN dit aussi, pour
IDisposable.Jetez
:Alors, qui est-il? Lequel est le lieu pour moi de nettoyage de ressources non managées? La réponse est:
Vous avez certainement pu le lieu de votre non géré nettoyage dans le finaliseur:
Le problème avec cela est que vous n'avez aucune idée de quand le garbage collector se déplacer à la finalisation de votre objet. Votre géré par les nations unies, onu-nécessaire, non utilisés natif de ressources va rester jusqu'à ce que le garbage collector finalement s'exécute. Puis il va appeler votre méthode finalizer, à nettoyer les ressources non managées. La documentation de Objet.Finaliser points: les
C'est la vertu de l'aide
Dispose
pour le nettoyage de ressources non managées; vous faire connaître et de contrôle, lorsque des ressources non managées sont nettoyé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 qui besoins pour se débarrasser de 530 MO d'images internes maintenant, car elles ne sont plus nécessaires. Quand on n'a pas: la machine broie à une permutation de s'arrêter.
Bonus De Lecture
Pour n'importe qui qui aime le style de cette réponse (explication de l' pourquoi, de sorte que le comment devient évident), je vous conseille de lire le Chapitre Un de Ne de la Boîte de COM Essentiel:
En 35 pages, il explique les problèmes de l'utilisation des objets binaires, et invente COM devant vos yeux. Une fois que vous vous rendez compte de la pourquoi de COM, le restant de 300 pages sont évidentes, et juste détail de Microsoft de la mise en œuvre.
Je pense que chaque programmeur qui a déjà fait affaire avec des objets ou des COM devrait, à tout le moins, de lire le premier chapitre. C'est la meilleure explication de quoi que ce soit jamais.
Bonus Supplémentaire De Lecture
Quand tout ce que vous savez est faux par Eric Lippert
readonly
signifiait une propriété a étéreadonly
. je vois maintenant qu'unreadonly
champ n'est surtoutreadonly
. La plupartreadonly
: légèrement non-readonly
. Si vous souhaitez faire un champreadonly
, alors je suppose que vous devrez trouver votre propre méthode pour vous protéger d'disposé des objets. Personnellement, je voudrais utiliser une propriété en lecture seule, plutôt que d'unreadonly
champ.GC.SuppressFinalize(this)
serait appelé si la personne à l'aide de votre objet en fait appeléDispose();
. Si ils n'ont pas le GC va encore venir (puisque nous emportait Finaliser) appelDispose(false);
de Sorte que vous toujours vérifier laIsItSafeToFreeManagedObjects
paramètreDispose(boolean)
. Si vous n'avez pas pris la peine de remplacer l'outil de finalisation, afin de Disposer d'appel dans le cas où l'utilisateur a oublié), puis non: vous n'auriez pas à vous soucier des objets gérés de ne plus être là.base.Dispose()
si votre objet descend d'un objet qui implémenteIDisposable
. Par exemple, si votre objet descend deSystem.Object
puis il y a n'est pas toutbase.Dispose()
pour vous appeler! j'ai seulement mentionné lesbase.Dispose()
que d'un côté "Note" (Avis il est cité, fond gris, texte). Il a été conçu comme un rappel, une note, un "n'oubliez pas de chose". Il ne s'applique pas dans tous les cas - ne peut donc pas être une règle. Vous pouvez/devez seulement appelerbase.Dispose()
si il y a est unbase.Dispose()
.Dispose
sur un objet si elle met en œuvreIDisposable
, puis l'faiblement objet référencé déjà a déjà été "éliminés". La résurrection de l'objet et de l'appel deDispose
est tout aussi incorrect que d'essayer d'appelerDispose
deux fois dos à dos. Un objet peut essayer de se défendre contre de tels abus. Un objet peut essayez aussi de la défendre contre les abus où l'appelant à ne pas faire appelDispose
. Mais si vous allez intentionnellement de ne pas appelerDispose
, alors vous êtes maintenant dans un monde où vous devez creuser dans le sans-papiers internes de la façon dont une classe prend en charge le nettoyage.null
. Tout d'abord, cela signifie que vous ne pouvez pas rendrereadonly
, et d'autre part, vous devez faire très moche!=null
de contrôle (comme dans l'exemple de code). Vous pourriez avoir un drapeaudisposed
, mais il est plus facile de ne pas poser de question. L' .NET GC est assez agressif qu'une référence à un champx
ne sera plus compté "utilisé" par le temps qu'il passe lax.Dispose()
ligne.Dispose
plus d'une fois. Si vous n'avez pas le drapeau qui disposent déjà arrivé (en quelque sorte) alors vous allez être par inadvertance (et à tort), l'appel d'Éliminer plus d'une fois sur les objets vous utilisation.Dispose
sur un objet de ne pas libérer l'objet (même s'il peut libérer de la mémoire de l'objet est l'aide). je voudrais modifier le libellé; mais je ne peux pas penser à quelque chose pour le changer pour qui n'est pas extraordinairement pédant.protected void Dispose(Boolean freeManagedObjectsAlso)
normalement être déclaré commevirtual
si nous mettons en place une classe de base? J'ai remarqué que dans la réponse ci-dessous, leurDispose(Boolean)
est virtuel.protected void Dispose(bool)
méthode doit être faite virtuel pour permettre aux classes dérivées de la capacité de raccordement dans le disposer processus. La forme canonique de la signature de cette méthode est pour le paramètre d'être nommédisposing
. C'est aussi typique que les ressources non gérées sont gérées une fois les ressources gérées.Dispose()
est réentrant. Vous n'avez aucun contrôle sur la façon dont de nombreuses fois, il pourrait être appelée, et elle doit jamais lancer une exception en raison du fait qu'il a été appelé plus d'une fois. Vous avez besoin de mettre en œuvre unprivate bool disposed
drapeau et de le vérifier dans leDispose(bool)
méthode avant de faire quoi que ce soit (et le régler si vous n'réelle de faire un peu de travail.)Dispose
- même si ces objets ont finaliseurs. Un finaliseur n'est même pas garanti à la fin du programme; l'exécution va essayer, mais certains types de fuites peuvent en fait s'avèrent être permanente. Donc, ne supposez pas qu'un outil de finalisation permettra d'éviter une fuite, et toujours utiliserusing
.IDisposable
. IDisposable n'existe, et a été créé spécialement pour, l'introduction de déterministe de la destruction de la sémantique pour un système qui n'a pas déterministe de la destruction. Alors qu'il est documenté comme existant pour les ressources non managées (comme c'était la justification de retour en 2000-2001) il n'est pas fait à cause des ressources non managées, qu'il existe.. il existe pour les "déterministe nettoyage de toutes les ressources".Foo
que "l'acquisition d'une ressource" lors de certains à l'extérieur de l'entité se met dans un moins-que-idéal pour Foo bénéficier, avec l'intention de rester dans cet état jusqu'à nouvel avis, et que "la libération d'une ressource" lorsqu'il donne un avis que ses services ne sont plus nécessaires, je dirais que s'inscritIDisposable
assez bien..Dispose
ou un finaliseur si vous n'avez pas les ressources non managées. Vous n'avez même pas besoin de mettre en œuvre un finaliseur même si vous ne ont des ressources non managées - il n'est là que pour aller au-dessus et au-delà pour attraper le développeur qui a foiré. Si vous avez des ressources non managées, vous avez seulement besoin de mettre en œuvre des certains méthode (par exemple,Dispose
) pour nettoyer ces ressources. La personne à l'aide de votre classe est obligatoire pour l'appeler quand il est fait à l'aide.Dispose()
ou le finaliseur peut être appelé à ce stade.databaseConnection
objet n'existe plus (parce que GC déjà repris que vers le haut) puis la façon, la conditionthis.databaseConnection != null
évaluer à vrai? Dans ce cas, il n'exécutez jamaisthis.databaseConnection.Dispose()
et plantage ne se produit pas.. Pourriez-vous me dire qu'est-ce qui me manque ici.databaseConnection
continuera d'exister lorsque l'utilisateur appelleDispose
, parce que votre objet a toujours une référence. Le garbage collector ne pas collecter des objets pendant qu'ils ont encore des références.Dispose
, il n'y a aucun moyen de GC déjà recueilli 'databaseConnection', alors comment se fait il y aura un crash lors de la prochaine ligne dethis.databaseConnection.Dispose()
est exécuté ? Ce Crash a été la principale raison pour laquelle nous avons introduit la troisième méthode..Droit?Dispose(bool)
à partir de l'outil de finalisation. Si votre objet est en cours de finalisation, puis tout ce que vous peut-être disparu. Mais quand le utilisateur appels en Disposer, nous savons que tout doit être bon, parce que le CLR ne serait pas finalisé tout ce que nous encore de référence. Une fois notre objet n'est plus référencé, toutes les références que nous avons tenues sont potentiellement disparu.IDisposable
est souvent utilisé pour exploiter lesusing
déclaration et de profiter d'un moyen facile de le faire déterministe de nettoyage d'objets gérés.Le but du modèle dispose est de fournir un mécanisme pour nettoyer à la fois réussi et les ressources non managées et lorsque cela se produit dépend de la façon dont la méthode dispose est appelé. Dans votre exemple, l'utilisation de Jeter n'est pas en train de faire tout ce qui est lié à disposer, depuis la compensation d'une liste n'a pas d'impact sur la collection en cours de cession. De même, les appels à définir les variables à null également ne pas avoir d'impact sur la GC.
Vous pouvez prendre un coup d'oeil à ce l'article pour plus de détails sur la façon de mettre en œuvre le modèle dispose, mais fondamentalement, ça ressemble à ça:
La méthode qui est le plus important ici est l'dispose(bool), qui en réalité s'exécute dans deux cas différents:
Le problème avec simplement laisser le GC prendre soin de faire le nettoyage, c'est que vous n'avez pas de réel contrôle sur le moment où le GC exécuter un cycle de collecte de données (vous pouvez appeler GC.Collect(), mais vous ne devriez vraiment pas), alors que les ressources peuvent rester plus longtemps que nécessaire. Rappelez-vous, en appelant Dispose() n'est pas réellement de causer un cycle de collecte ou de quelque façon que provoquer la GC pour les collecter, de les gratuit de l'objet; il fournit simplement le moyen de deterministicly nettoyage des ressources utilisées et de dire la GC que ce nettoyage a déjà été effectué.
Le point de l'ensemble de IDisposable et le modèle dispose n'est pas immédiatement libérer de la mémoire. Le seul moment où un appel à Disposer va en fait de même avoir une chance de faire immédiatement la libération de la mémoire, c'est quand il est de la manipulation de l'élimination == false scénario et la manipulation des ressources non managées. Pour le code managé, la mémoire n'est pas réellement être récupéré jusqu'à ce que le GC exécute un cycle de collecte, de laquelle vous avez vraiment n'avez aucun contrôle sur (autres que l'appel d'GC.Collect(), qui comme je l'ai déjà mentionné, c'est pas une bonne idée).
Votre scénario n'est pas vraiment valable depuis les chaînes de dans .NET ne pas utiliser n'importe quel unamanged ressources et de ne pas mettre en œuvre IDisposable, il n'y a aucun moyen de les forcer à être "nettoyé."
Il ne devrait plus les appels à un objet de méthodes d'Aliéner a été appelée sur elle (même si un objet doit tolérer appelle en outre à Disposition). Par conséquent, l'exemple de la question est stupide. Si Disposer est appelée, l'objet lui-même peut être mis au rebut. Ainsi l'utilisateur doit juste jeter toutes les références à cet objet entier (mis à null) et de tous les objets liés à l'interne il sera automatiquement nettoyé.
Comme pour la question générale sur les sites gérés et non gérés, et la discussion dans d'autres réponses, je pense que la réponse à cette question doit commencer par une définition d'une ressource non managée.
Ce que 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 ramener à sortir de cet état. Maintenant, dans le typique exemple, le premier peut-être une fonction qui renvoie un descripteur de fichier, et le second pourrait être un appel à
CloseHandle
.Mais - et c'est la clé, ils pourraient être n'importe quelle paire de fonctions. On construit un etat, l'autre l'arrache. Si l'état a été construit, mais pas déchiré vers le bas encore, puis une instance de la ressource existe. Vous devez prendre des dispositions pour le démontage pour arriver au bon moment - la ressource n'est pas géré par le CLR. La seule automatiquement géré type de ressource est la mémoire. Il en existe deux types: le GC, et la pile. Types de valeur sont gérés par la pile (ou par baladait à l'intérieur de types de référence), et les types de référence sont gérés par le GC.
Ces fonctions peuvent provoquer des modifications de l'état qui peuvent être librement entrelacés, ou peut-être besoin d'être parfaitement imbriqués. Les changements d'état peuvent être thread-safe, ou ils ne pourraient pas.
Regardez l'exemple dans la Justice de la question. Les modifications dans le fichier Journal de l'indentation doit être parfaitement imbriqués, ou tout va mal. En outre, ils sont peu susceptibles d'être thread-safe.
Il est possible de faire du stop avec le garbage collector pour obtenir votre des ressources non managées nettoyé. Mais seulement si le changement d'état des fonctions sont thread-safe et deux états peuvent avoir des durées de vie qui se chevauchent en aucune façon. Alors que la Justice l'exemple d'une ressource ne doit PAS avoir un finaliseur! Il n'aurait tout simplement pas aider tout le monde.
Pour ces types de ressources, vous pouvez simplement mettre en œuvre
IDisposable
, sans un finaliseur. Le finalizer est absolument facultatif - il doit l'être. C'est l'estomper ou même pas mentionné dans de nombreux ouvrages.Ensuite, vous devez utiliser le
using
instruction pour avoir une chance de s'assurer queDispose
est appelé. C'est pratiquement comme se baladait avec la pile (donc comme finalizer est à la GC,using
est à la pile).La partie manquante est que vous avez à écrire manuellement Éliminer et de faire appel sur vos champs et de votre classe de base. C++/CLI, les programmeurs ne pas avoir à le faire. Le compilateur écrit pour eux dans la plupart des cas.
Il y a une alternative, que je préfère pour les états qui s'emboîtent parfaitement et ne sont pas thread-safe (en dehors de toute autre chose, en évitant IDisposable vous épargne le problème d'avoir une dispute 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 rappel:
Et puis un exemple simple serait:
Le lambda étant passé en sert comme d'un bloc de code, donc c'est comme vous faire votre propre structure de contrôle pour servir le même but que
using
, sauf que vous n'avez plus aucun danger de l'appelant en abuser. Il n'ya aucun moyen qu'ils peuvent échouer pour nettoyer la ressource.Cette technique est moins utile si la ressource est le genre qui peut avoir cumul des durées de vie, parce qu'alors, vous voulez être en mesure de construire des ressources A, puis B de ressource, puis de les tuer ressource et puis plus tard, tuer des ressources B. Vous ne pouvez pas faire cela si vous avez forcé à l'utilisateur de parfaitement nid comme ça. Mais alors vous devez utiliser
IDisposable
(mais toujours sans un finaliseur, sauf si vous avez mis en œuvre threadsafety, qui n'est pas gratuit).enter
etexit
est la base de la façon dont je pense à une ressource. Abonnement/désabonnement sur les événements s'inscrivent dans que sans difficulté. En termes de orthogonale/fongibles caractéristiques, il est pratiquement impossible à distinguer d'une fuite de mémoire. (Ceci n'est pas surprenant puisque l'abonnement est juste d'ajouter des objets à une liste.)Scénarios-je faire usage de IDisposable: nettoyer les ressources non managées, désabonnez-vous pour des événements, des liens étroits
Le langage que j'utilise pour la mise en œuvre de IDisposable (pas thread-safe):
Yep, que le code est complètement redondant et inutile, et il ne fait pas le garbage collector n'importe quoi qu'il ne serait pas faire autrement (une fois qu'une instance de MyCollection est hors de portée, ce qui est.) Surtout la
.Clear()
appels.Réponse à votre edit: en quelque Sorte. Si je fais ceci:
Il est fonctionnellement identique à ce à des fins de gestion de la mémoire:
Si vous avez vraiment vraiment vraiment besoin de libérer de la mémoire, ce même instant, l'appel
GC.Collect()
. Il n'y a pas de raison de le faire ici. La mémoire sera libérée lorsque cela est nécessaire.Si
MyCollection
va être nettoyée de toute façon, alors vous ne devriez pas avoir à jeter. Vous risqueriez juste de désabonnement de la CPU plus que nécessaire, et peut même annuler certains pré-calculées de l'analyse que le garbage collector a déjà effectué.- Je utiliser
IDisposable
de faire des choses comme assurer les threads sont éliminés correctement, avec des ressources non managées.MODIFIER En réponse à Scott commentaire:
Sur le plan conceptuel, la GC maintient une vue de l'objet graphique de référence, et toutes les références à partir de la pile d'images de threads. Ce segment peut être assez grande, et s'étendre sur plusieurs pages de la mémoire. Comme une optimisation, le GC cache son analyse des pages qui sont peu susceptibles de changer très souvent pour éviter une nouvelle analyse de la page inutilement. Le GC reçoit un avis du noyau lorsque les données d'une page de changements, de sorte qu'il sait que la page est sale et nécessite une nouvelle analyse. Si la collection est en Gen0 alors il est probable que d'autres choses dans la page sont en train de changer aussi, mais c'est moins probable dans Gen1 et Gen2. Pour l'anecdote, ces crochets ne sont pas disponibles dans Mac OS X pour l'équipe qui a porté le GC pour Mac afin d'obtenir le plug-in Silverlight de travail sur cette plate-forme.
Un autre point contre inutile disposition de ressources: imaginer une situation où un processus est en cours de déchargement. Imaginez également que le processus a été en cours d'exécution pendant un certain temps. Les Chances sont que beaucoup de mémoire de ce processus de pages ont été échangés sur le disque. À tout le moins, elles ne sont plus en L1 ou L2 cache. Dans une telle situation, il n'y a aucun point pour une application de déchargement pour échanger toutes ces données et le code des pages en mémoire de "libérer" les ressources qui vont être libérés par le système d'exploitation de toute façon lorsque le processus se termine. Cela s'applique à géré et même certaines ressources non managées. Seules les ressources garder les threads d'arrière-plan en vie doivent être éliminés, sinon le processus restera vivant.
Maintenant, lors de l'exécution normale il y éphémères sont des ressources qui doivent être nettoyés correctement (comme @fezmonkey souligne connexions de base de données, des sockets, des poignées de fenêtre) pour éviter de non managé des fuites de mémoire. Ce sont le genre de choses qui doivent être éliminés. Si vous créez une classe qui possède un thread (et par propriétaire, je veux dire qu'il a créé et qu'il est donc responsable de veiller à ce qu'il s'arrête, au moins par mon style de codage), alors que la classe la plus probable doit mettre en œuvre
IDisposable
et d'arracher le fil au cours deDispose
.L' .NET framework utilise le
IDisposable
interface comme un signal, même avertissement, pour que les développeurs de cette classe doit être éliminés. Je ne peux pas penser à tout types dans le cadre de mettre en œuvreIDisposable
(à l'exclusion explicite des implémentations d'interface) où l'immersion est facultatif.Dispose()
appels, voir: stackoverflow.com/questions/913228/...Si vous voulez supprimer dès maintenant, utilisez mémoire non managée.
Voir:
Dans l'exemple que vous avez posté, il n'est toujours pas "libérer de la mémoire, maintenant". Toute la mémoire garbage collecté, mais il peut permettre à la mémoire pour être recueilli dans un précédent génération. Vous auriez à faire quelques tests pour être sûr.
Le Cadre des lignes Directrices de Conception sont des lignes directrices et non des règles. Ils vous disent ce que l'interface est principalement, quand l'utiliser, comment l'utiliser et quand ne pas utiliser.
J'ai lu une fois le code qui était un simple RollBack() en cas d'erreur en utilisant IDisposable. Le MiniTx de classe ci-dessous serait de vérifier un drapeau sur Dispose() et si le
Commit
appel n'est jamais arrivé il serait alors appelRollback
sur lui-même. Elle a ajouté une couche d'indirection faire le code d'appel est beaucoup plus facile à comprendre et à maintenir. Le résultat cherché quelque chose comme:J'ai aussi vu le timing /code d'enregistrement de faire la même chose. Dans ce cas, la méthode dispose() arrêté le chronomètre et connecté que le bloc avait quitté.
Donc, ici, sont quelques exemples de mesures concrètes qui ne le font pas tout non managé des ressources de nettoyage, mais n'en a utilisé avec succès IDisposable pour créer nettoyeur de code.
Je ne vais pas répéter les trucs habituels sur l'Utilisation ou de libérer géré par les nations unies de ressources, que tout a été couvert. Mais je tiens à souligner ce qui me semble une idée fausse commune.
Étant donné le code suivant
Je me rends compte que les Jetables mise en œuvre ne suit pas les lignes directrices actuelles, mais j'espère que vous tous obtenez l'idée.
Maintenant, lorsque Disposer est appelé, la quantité de mémoire obtient libéré?
Réponse: Aucune.
Appeler dispose peut libérer des ressources non managées, il NE peut pas récupérer la mémoire gérée, seulement le GC peut le faire. Thats pour ne pas dire que ce n'est pas une bonne idée, en suivant le schéma ci-dessus est toujours une bonne idée en fait. Une fois Aliéner a été exécuté, il n'en est rien, l'arrêt de la GC réclamer la mémoire qui a été utilisée par _Large, même si l'instance de LargeStuff peut encore être portée. Les chaînes de _Large peut aussi être dans la génération 0, mais l'instance de LargeStuff pourrait être gen 2, donc encore une fois, la mémoire serait ré-affirmé plus tôt.
Il n'y a pas de point dans l'ajout d'un finaliser appeler la méthode dispose indiqué ci-dessus si. Cela ne fera que RETARDER la re-revendication de la mémoire pour permettre de le finaliser à exécuter.
LargeStuff
a été autour assez longtemps pour le faire à la Génération 2, et si_Large
contient une référence à une nouvelle chaîne qui est dans la Génération 0, alors, si l'instance deLargeStuff
est abandonné sans micropression hors_Large
, puis de la chaîne visée par_Large
seront conservés jusqu'à ce que la prochaine Gen2 collection. Réinitialisation_Large
peut laisser la chaîne supprimées lors de la prochaine Gen0 collection. Dans la plupart des cas, micropression des références n'est pas utile, mais il y a des cas où il peut offrir certains avantages.En dehors de son utilisation principale comme un moyen de contrôle de la vie de ressources système (entièrement couverts par la réponse impressionnante de Ian, bravo!), le IDisposable/à l'aide de combo peut également être utilisé pour champ d'application de la modification de l'état de la (critique) des ressources mondiales: le console, le fils, le processus, tout objet global comme un instance d'application.
J'ai écrit un article à propos de 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 certaines souvent utilisés l'état global dans un réutilisables et lisible manière: console de couleurs, actuel fil de la culture, Excel propriétés de l'objet application...
Si quoi que ce soit, j'imagine que le code à moins efficace que lorsque les laissant.
L'appel de la Clear() les méthodes sont inutiles, et que le GC ne serait probablement pas faire que si les Éliminer ne l'ai pas fait...
Il y a des choses que l'
Dispose()
opération dans l'exemple de code que pourrait ont un effet qui ne serait pas due à une normale GC de laMyCollection
objet.Si les objets référencés par
_theList
ou_theDict
sont visés par d'autres objets, alors queList<>
ouDictionary<>
objet ne sera pas soumis à la collection, mais soudain ont pas de contenu. Si il n'y avait pas Dispose() de l'opération, comme dans l'exemple, ces collections contiennent encore de leur contenu.Bien sûr, si c'était la situation je dirais que c'est une fracture de la conception, je suis juste rappelé manière affectée, je suppose) que la
Dispose()
opération risque de ne pas être complètement redondant, selon qu'il y a d'autres utilisations de laList<>
ouDictionary<>
qui ne sont pas affichés dans le fragment.Un problème avec la plupart des discussions de "ressources non managées", c'est qu'ils n'ont pas vraiment de définir le terme, mais semblent impliquer qu'il a quelque chose à faire avec du code non managé. S'il est vrai que de nombreux types de ressources non managées faire l'interface avec le code non managé, la pensée de ressources non managées dans de telles conditions n'est pas utile.
Au lieu de cela, il faut reconnaître que toutes les ressources gérées ont en commun: ils entraînent un objet à poser à l'extérieur "chose" à faire quelque chose en son nom, au détriment de certains autres "choses", et de l'autre entité acceptant de le faire jusqu'à nouvel avis. Si l'objet était abandonnée et disparaissent sans laisser de trace, rien n'aurait pu dire qu'en dehors de "chose" qu'il n'est plus nécessaire de modifier son comportement sur le nom de l'objet qui n'existait plus; par conséquent, la chose de l'utilité serait définitivement diminuée.
Une ressource non managée, puis, représente un accord par certains à l'extérieur de chose à modifier son comportement sur le nom d'un objet, qui serait inutile de nuire à l'utilité de l'extérieur "chose" si l'objet ont été abandonnés et a cessé d'exister. Une gestion de la ressource est un objet qui en est le bénéficiaire d'un tel accord, mais qui a signé jusqu'à recevoir une notification si elle est abandonnée, et qui va utiliser cette notification, pour mettre ses affaires en ordre avant qu'il soit détruit.
IDisposable
est bon pour vous désabonner de ces événements.D'abord de la définition. Pour moi non managé des ressources signifie qu'une classe qui implémente l'interface IDisposable ou quelque chose de créé avec l'utilisation des appels de dll. GC ne savent pas comment faire face à de tels objets. Si la classe a, par exemple, seuls les types de valeur, puis je ne pas considérer cette classe comme une classe avec des ressources non managées.
Pour mon code, je l'ai suivi de nouvelles pratiques:
Modèle suivant montre ce que j'ai décrit dans les mots de l'échantillon de code:
is IDisposable
doit lui-même être considéré comme une ressource non managée? Cela ne semble pas correcte. Aussi, si le implmenting type est un pur type de valeur que vous semblez suggérer qu'il n'a pas besoin d'être éliminés. Cela semble aussi être mauvais.Les plus justifiable en cas d'utilisation pour l'élimination de la gestion des ressources, la préparation de la conférence de récupérer des ressources qui seraient autrement jamais être collecté.
En est un excellent exemple des références circulaires.
Tandis qu'il est préférable d'utiliser des modèles qui permettent d'éviter les références circulaires, si vous vous retrouvez avec (par exemple) un 'enfant' objet qui a une référence à ses "parents", ce qui peut arrêter GC collection de la société mère si vous venez d'abandonner la référence et de s'appuyer sur GC - de plus, si vous avez mis en place un outil de finalisation, ce ne sera jamais appelé.
Le seul chemin de ronde, c'est manuellement rompre les références circulaires en définissant le Parent références à null sur les enfants.
La mise en œuvre de IDisposable des parents et des enfants est la meilleure façon de le faire. Lorsque Disposer est appelée sur le Parent, appelez dispose sur tous les Enfants, et à l'enfant de Disposer de la méthode, définir le Parent références à null.
WeakReference
, le système va vérifier un indicateur qui indique qu'un live enracinée de référence a été trouvé dans le dernier cycle de GC, et pour ajouter l'objet d'une file d'attente d'objets ayant besoin immédiat de finalisation, la libération de l'objet à partir de la grand tas d'objets, ou d'invalider la référence faible. Circulaire refs ne sera pas garder les objets en vie si pas d'autres références existent.Votre exemple de code n'est pas un bon exemple pour
IDisposable
d'utilisation. Dictionnaire de compensation normalement ne devrait pas aller à l'Dispose
méthode. Des éléments dans le dictionnaire sera effacée et disposé quand elle est hors de portée.IDisposable
mise en œuvre est nécessaire pour libérer de la mémoire/des gestionnaires qui ne sera pas de presse/libre, même après qu'ils hors de portée.L'exemple suivant montre un bon exemple pour IDisposable modèle avec un peu de code et des commentaires.
Je vois beaucoup de réponses ont commencé à parler à l'aide de IDisposable administrés et des ressources non managées. Je vous suggère cet article comme l'une des meilleures explications que j'ai trouvé pour combien de 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 des objets gérés qui prennent beaucoup de mémoire, la réponse courte serait pas. La raison en est que, une fois que vous disposez d'un IDisposable vous devriez le laisser aller hors de portée. À ce point, tout enfant référencé objets sont hors de portée et aurez recueillis.
La seule véritable exception à cette règle serait si vous avez beaucoup de mémoire liée à des objets gérés et que vous avez bloqué le thread de l'attente d'une opération. Si ces objets où ne va pas être nécessaire après que l'appel est terminé, puis la création de ces références à la valeur null peut permettre le garbage collector pour collecter plus tôt. Mais ce scénario se représenter mauvais code que nécessaire pour être réusiné - pas un cas d'utilisation de IDisposable.