2 votes

Dans un environnement géré, pourquoi avons-nous besoin de IDisposable ?

L'un des principaux avantages du code géré est la gestion intégrée de la mémoire. Vous n'avez pas besoin de suivre les pointeurs, la taille des tampons, de libérer la mémoire avec laquelle vous avez terminé, etc. géré aspect le fait pour vous.

Alors pourquoi avons-nous un IDisposable l'interface ? MSDN dit l'interface permet de traiter les ressources non gérées telles que les poignées de fenêtre, les fichiers, etc. Mais pourquoi me demander d'appeler explicitement l'interface Dispose (ou utiliser la méthode Using ) ?

  1. Pourquoi le CLR ne peut-il pas suivre le moment où l'objet sort du champ d'application et appeler Dispose automatiquement ?

    Public Function DoSomething() As String Dim reader As New StreamReader("myfile.txt") Dim txtFromFile As String = reader.ReadToEnd()

    Return txtFromFile '<==== reader goes out of scope after this line, so call Dispose automatically

    End Function

  2. À tout le moins, pourquoi le ramasse-miettes ne finira-t-il pas par l'atteindre et par appeler Dispose ?

Qu'est-ce qui me manque ?

EDIT

Plusieurs personnes (ici et dans d'autres réponses qui suggèrent Using ) ont suggéré que la collecte des déchets n'est pas suffisante parce que le GC ne fait qu'une partie du travail. éventuellement se charge de collecter les IDisposable . Je ne comprends pas pourquoi cet argument fait la distinction entre une IDisposable et tout autre objet en .NET. Et avant de dire IDisposable sont plus gourmands en ressources :

  1. En MSDN ci-dessus dit IDisposable est destiné aux objets non gérés, quels que soient leurs besoins en ressources
  2. J'ai vu des objets .NET très gourmands en ressources (que diriez-vous de System.Web.UI.Page o System.Data.Objects.ObjectContext ).

2voto

Damien_The_Unbeliever Points 102139

Comme je l'ai dit dans le commentaire, le CLR ne sait pas quand les objets sortent du champ d'application.

Prenons votre exemple :

Public Function DoSomething() As String
    Dim reader As New StreamReader("myfile.txt")
    Dim txtFromFile As String = reader.ReadToEnd()

    Return txtFromFile '<==== reader goes out of scope after this line, so call Dispose automatically
End Function

Ce qu'il faudrait en fait, c'est qu'il analyse tout le corps de la méthode, pour vérifier si vous avez transmis une référence à ce lecteur à une autre méthode, ou si vous avez stocké une référence dans, disons, un champ.

Il s'agirait entonces doit déterminer si cette autre méthode a stocké la référence quelque part, ou si elle a appelé d'autres méthodes, etc.

Il s'agirait entonces doivent déduire si la référence est stockée ailleurs, si l'intention est que quelque chose d'autre utilise plus tard cette référence et s'attende à y trouver une instance non éliminée.

Comparez cela avec votre connaissances. Vous savoir (avec un peu de chance) si vous avez stocké une référence ailleurs ou si vous avez transmis la référence à quelque chose d'autre qui a "pris possession" de ce jetable. Si vous savez que ni l'un ni l'autre ne s'est produit, vous pouvez transmettre cette connaissance au compilateur - en ajoutant une ligne de commande Using bloc.


En tout état de cause, le ramasse-miettes ne finira-t-il pas par s'en emparer et par appeler Dispose ?

Si a) l'objet "contient" directement une ressource non gérée, et b) la personne qui a mis en œuvre cet objet suit les meilleures pratiques, elle devrait avoir mis en œuvre un finaliseur sur cet objet qui effectuera le nettoyage des ressources non gérées (généralement en appelant une méthode partagée entre le finaliseur et la méthode Dispose ).

Cependant, vous n'avez aucune idée quand la prochaine collecte d'ordures. Entre-temps, vous pouvez refuser l'accès à la même ressource non gérée à d'autres programmes - ou même à une autre partie de votre propre programme. Vous devez traiter les ressources non gérées comme des ressources rares. Si quelqu'un a implémenté Dispose ils veulent que vous l'appeliez (soit explicitement, soit par l'intermédiaire de l'option Using ) lorsque vous savez que vous n'avez plus besoin d'accéder à cette ressource.

1voto

jalf Points 142628

Le CLR ne suit pas le moment où un objet sort du champ d'application. L'intérêt d'un ramasse-miettes est qu'il va éventuellement collecter les objets morts. Mais le mot clé est "éventuellement". Vous n'avez aucune garantie quant à quand Si vous avez besoin qu'il se produise à un moment précis, vous devez donc faire quelque chose pour qu'il se produise. C'est ce que fait le Dispose() y using sont destinés à.

En général, le CLR ne peut pas suivre lorsqu'un objet sort du champ d'application, car s'il n'existe plus dans le champ d'application où il a été créé, une référence peut avoir été transmise à une autre fonction dans un autre champ d'application, voire dans un autre thread.

Lorsque vous créez un objet, vous obtenez simplement une référence à cet objet. Une référence comme une autre. L'objet ne vit pas dans la portée actuelle, il n'a pas d'attachement particulier au site où il a été créé.

Ceci est différent de la situation en C++, où lorsqu'un objet est créé, c'est là qu'il vit, et le destructeur sera appelé lorsque vous quitterez l'objet. que (et si des références à l'objet existent ailleurs, pas de chance)

Il s'agit donc d'une situation où il faut être un peu plus prudent et écrire un peu plus de code dans un environnement de collecte de déchets. Vous ne pouvez pas compter sur le fait que la durée de vie d'une ressource est limitée par la portée dans laquelle elle est déclarée, vous devez donc explicitement dire quand elle doit être éliminée.

1voto

supercat Points 25534

Le langage C++ a été conçu pour déterminer exactement quand les choses entrent et sortent du champ d'application. Il s'en sortait très bien dans les situations où les objets avaient un "propriétaire" clairement identifiable. Malheureusement, il existe de nombreuses situations où les objets n'ont pas de propriétaire particulier. Par exemple, si un objet Foo génère une chaîne contenant les caractères "Hello" et la transmet à un autre objet Bar qui stocke une référence à celle-ci, ni Foo ni Bar n'aurait aucun moyen de savoir si et quand l'autre objet n'a plus besoin de la chaîne. Le C++ peut gérer cette situation en associant un compteur à chaque chaîne de caractères, en faisant en sorte que toute méthode qui crée une référence incrémente ce compteur et que toute méthode qui abandonne une référence le décrémente et, si le compteur atteint zéro, supprime l'objet. Malheureusement, exiger des méthodes qu'elles incrémentent et décrémentent les compteurs chaque fois qu'elles transmettent des références à un objet peut s'avérer coûteux, en particulier sur les machines multiprocesseurs (puisqu'il est nécessaire de s'assurer que si, par exemple, un compteur est égal à 3 et que deux processeurs créent simultanément une référence à l'objet, l'un fera passer le compteur de 3 à 4 et l'autre de 4 à 5, au lieu que les deux processeurs constatent que le compteur est à 3 et le fixent tous les deux à 4, ce qui serait un véritable désastre).

Un système de collecte de déchets évite ce problème en demandant simplement aux programmes de faire circuler des références sans se soucier de savoir quand la dernière référence à un objet est abandonnée ou écrasée. Chaque fois que le système décide qu'il doit essayer de réutiliser de la mémoire, il peut tout arrêter pendant un moment, examiner quels objets ont encore des références vivantes et libérer toute la mémoire utilisée par des objets qui ne sont plus nécessaires. Tous les processus doivent être synchronisés lorsque le système décide de lancer un cycle de ramassage de déchets (les systèmes diffèrent quant à la durée pendant laquelle tout doit être gelé), mais le coût de cette synchronisation peut être bien inférieur à celui de la synchronisation inter-processeurs à chaque fois qu'une routine transmet une référence à une autre.

Les systèmes de collecte de déchets fonctionnent bien pour la gestion de la mémoire. Libérer la mémoire utilisée par un objet ne servira à rien tant que cette mémoire ne sera pas utilisée pour quelque chose si elle est libérée. Le fait que les applications qui n'ont pas besoin d'utiliser beaucoup de mémoire puissent passer un temps arbitrairement long sans effectuer de ramassage des ordures est une bonne chose, car il n'y a pas de mal à ce qu'un objet reste inutilement en mémoire lorsqu'il n'y a rien d'autre qui en a besoin de toute façon. Malheureusement, le fait qu'une application n'utilise pas beaucoup de mémoire n'implique pas qu'il n'y a pas d'objets abandonnés qui ont l'usage exclusif de ressources autres que la mémoire (par exemple des fichiers ouverts pour un accès exclusif) et qui empêchent d'autres utilisateurs potentiels de ces ressources d'y accéder. La notification systématique des objets lorsqu'ils ne sont plus nécessaires est donc nécessaire pour garantir que les objets qui ont acquis des ressources exclusives ne les conservent pas trop longtemps.

1voto

Afin de compiler toutes les informations utiles des autres réponses en un seul endroit, voici ce que je pense être les clés pour comprendre pourquoi .NET est une technologie de pointe. besoins un IDisposable .

Disponibilité des ressources

  1. Le système de collecte des déchets (c'est-à-dire le "code géré") est conçu pour et très bonne gestion de la mémoire.
  2. Il existe d'autres types de ressources système, comme les gestionnaires de fichiers, les connexions aux bases de données, les sockets, Windows, les processus, etc.
  3. L'une des principales caractéristiques du GC est qu'il laisse de la mémoire abandonnée jusqu'à ce que autre chose a besoin de mémoire au lieu de la nettoyer dès que possible. C'est une bonne chose pour la mémoire (qui se soucie de ce qu'elle fait lorsqu'elle n'est pas utilisée), mais ce n'est pas le cas pour les autres ressources. Celles-ci dépendent du fait qu'elles soient listées comme étant disponibles lorsqu'elles le sont réellement.
  4. Sans IDisposable Les ressources non-mémoire (fichiers, etc.) ne deviennent disponibles que lorsque le GC finalise l'objet qui les a acquises, c'est-à-dire lorsque le système a besoin de l'espace mémoire de cet objet. Cela peut prendre a) beaucoup de temps, ou b) jamais.

Finalisation

  1. Le GC finalise les objets dans un ordre non déterministe. Pire encore, il sépare les parties gérées et non gérées de la finalisation des objets.
  2. Cela signifie que le GC peut finaliser les parties gérées d'un objet avant les parties non gérées. Si les parties non gérées tentent de rappeler les parties gérées...

Structure du code

  1. Il n'y a pas de bon moyen pour que le compilateur pour savoir quand un objet est "abandonné".
  2. A Using fournit un champ d'application spécialisé à une variable pour informer le compilateur : "Après la fin de ce bloc, j'abandonne cet objet".
  3. Ainsi, la responsabilité de la gestion des références incombe à l programmeur au lieu du CLR (où il se trouve normalement). Si vous passez une ressource à partir d'un Using vous obtiendrez probablement un NullReferenceException
  4. Pour les raisons évoquées plus haut, cette charge doit réellement incomber au programmeur.

0voto

Zev Spitz Points 2402

Si vous avez une fonction qui tourne depuis longtemps, ou si vous avez initialisé un champ dans une classe de longue durée à l'aide d'une ressource externe, il est probablement déconseillé d'attendre que la variable/le champ sorte du champ d'application.

En outre, si un champ statique a été initialisé de la sorte, il ne sortira jamais de son champ d'application.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X