Que se passe-t-il si je n'appelle pas Dispose
sur le pen
dans cet extrait de code ?
private void panel_Paint(object sender, PaintEventArgs e)
{
var pen = Pen(Color.White, 1);
//Do some drawing
}
Que se passe-t-il si je n'appelle pas Dispose
sur le pen
dans cet extrait de code ?
private void panel_Paint(object sender, PaintEventArgs e)
{
var pen = Pen(Color.White, 1);
//Do some drawing
}
Quelques corrections doivent être apportées ici :
Concernant la réponse de Phil Devaney :
"...L'appel à Dispose vous permet de faire un nettoyage déterministe et est fortement recommandé."
En fait, l'appel à Dispose() ne provoque pas de façon déterministe une collecte GC dans .NET - c'est-à-dire qu'il ne déclenche PAS immédiatement une GC juste parce que vous avez appelé Dispose(). Il ne fait que signaler indirectement au GC que l'objet peut être nettoyé lors du prochain GC (pour la génération dans laquelle l'objet se trouve). En d'autres termes, si l'objet vit dans la génération 1, il ne sera pas éliminé avant qu'une collecte de génération 1 ait lieu. L'une des seules façons (mais pas la seule) de faire en sorte que le GC effectue une collecte de manière programmée et déterministe est d'appeler GC.Collect(). Cependant, il n'est pas recommandé d'agir ainsi, car le GC s'auto-ajuste pendant l'exécution en collectant des données sur les allocations de mémoire pendant l'exécution de votre application. L'appel à GC.Collect() vide ces mesures et oblige le GC à recommencer son "réglage".
En ce qui concerne la réponse :
IDisposable est pour jeter non géré ressources. C'est le modèle de .NET.
C'est incomplet. Comme la GC n'est pas déterministe, le motif Dispose, ( Comment implémenter correctement le pattern Dispose ), est disponible afin que vous puissiez libérer les ressources que vous utilisez - gérées ou non. Il a rien à faire avec le type de ressources que vous libérez. La nécessité d'implémenter un Finalizer est liée au type de ressources que vous utilisez - c'est-à-dire qu'il faut en implémenter UNIQUEMENT si vous avez des ressources non finalisables (c'est-à-dire natives). Vous confondez peut-être les deux. BTW, vous devriez éviter d'implémenter un Finalizer en utilisant la classe SafeHandle à la place qui enveloppe les ressources natives qui sont marshalées via P/Invoke ou COM Interop. Si vous finissez par mettre en œuvre un Finalizer, vous devez siempre mettre en œuvre le modèle de dépôt.
Une note critique que personne n'a encore mentionnée est que si un objet jetable est créé et qu'il a un Finalizer (et vous ne savez jamais vraiment s'ils en ont un - et vous ne devriez certainement pas faire d'hypothèses à ce sujet), alors il sera envoyé directement à la Finalization Queue et vivra pour au moins une collecte GC supplémentaire. .
Si GC.SuppressFinalize() n'est pas finalement appelé, le finalisateur de l'objet sera appelé lors de la prochaine GC. Notez qu'une implémentation correcte du pattern Dispose devrait appeler GC.SuppressFinalize(). Ainsi, si vous appelez Dispose() sur l'objet, et qu'il a implémenté le pattern correctement, vous éviterez l'exécution du Finalizer. Si vous n'appelez pas Dispose() sur un objet qui a un finalizer, l'objet aura son Finalizer exécuté par le GC sur la prochaine collection. Pourquoi est-ce mauvais ? Le thread du Finalizer dans le CLR jusqu'à .NET 4.6 inclus est monofilaire. Imaginez ce qui se passe si vous augmentez la charge de travail de ce thread - les performances de votre application s'envolent vers vous savez où.
L'appel de Dispose sur un objet permet d'effectuer les opérations suivantes :
Modifier : Je viens de remarquer que la documentation MSDN "tout savoir et toujours correcte" sur IDisposable (sarcasme extrême ici) dit effectivement
L'utilisation principale de cette interface est de libérer les ressources non gérées
Comme tout le monde devrait le savoir, MSDN est loin d'être correct, ne mentionne ou ne montre jamais les "meilleures pratiques", fournit parfois des exemples qui ne compilent pas, etc. Il est regrettable que cela soit documenté dans ces termes. Cependant, je sais ce qu'ils essayaient de dire : dans un monde parfait, le GC nettoiera tous les fichiers de la base de données. géré ressources pour vous (comme c'est idéaliste) ; il ne nettoiera cependant pas non géré ressources. C'est tout à fait vrai. Ceci étant dit, la vie n'est pas parfaite et aucune application ne l'est non plus. Le GC ne nettoiera que les ressources qui n'ont pas de références enracinées. C'est principalement là que se situe le problème.
Parmi les 15 à 20 façons différentes dont .NET peut faire "fuir" (ou ne pas libérer) de la mémoire, celle qui est la plus susceptible de vous mordre si vous n'appelez pas Dispose() est l'incapacité à désenregistrer/désenregistrer/désenregistrer/détacher les gestionnaires d'événements/délégués. Si vous créez un objet auquel des délégués sont connectés et que vous n'appelez pas Dispose() (et que vous ne détachez pas les délégués vous-même), le GC verra toujours l'objet comme ayant des références enracinées - c'est-à-dire les délégués. Ainsi, le GC ne le collectera jamais.
Commentaire/question de @joren ci-dessous (ma réponse est trop longue pour un commentaire) :
J'ai un article de blog sur le motif Dispose que je recommande d'utiliser - ( Comment implémenter correctement le pattern Dispose ). Il y a des moments où vous devriez annuler les références et cela ne fait jamais de mal de le faire. En fait, cela fait quelque chose avant l'exécution du GC - cela supprime la référence racine de cet objet. Plus tard, la GC balaie sa collection de références enracinées et collecte celles qui n'ont pas de référence enracinée. Pensez à cet exemple où il est bon de le faire : vous avez une instance de type "ClassA" - appelons-la "X". X contient un objet de type "ClassB" - appelons-le "Y". Y implémente IDisposable, donc, X devrait faire de même pour se débarrasser de Y. Supposons que X est dans la génération 2 ou la LOH et que Y est dans la génération 0 ou 1. Lorsque Dispose() est appelé sur X et que cette implémentation annule la référence à Y, la référence racine à Y est immédiatement supprimée. Si une GC se produit pour la Génération 0 ou la Génération 1, la mémoire/les ressources de Y sont nettoyées mais la mémoire/les ressources de X ne le sont pas puisque X vit dans la Génération 2 ou la LOH.
En ce qui concerne une contradiction apparente, Jason a déclaré "IDisposable sert à éliminer les ressources non gérées". Son seul but est de nettoyer de façon déterministe les ressources - qu'elles soient gérées ou non. L'implémentation correcte du pattern Dispose vous permet de nettoyer vos ressources. Je ne dis pas que les ressources non gérées ne sont pas nettoyées, mais plutôt que le but d'IDisposable est orthogonal au type de ressources dont vous disposez. On pourrait déduire de sa déclaration que vous n'avez pas besoin d'implémenter IDisposable à moins que vous n'utilisiez une ressource native - ce qui est absolument faux.
Merci de clarifier. Je suis d'accord avec vous pour dire que l'on pourrait être amené à cette fausse conclusion sur la base d'une lecture superficielle. Selon moi, le manque d'exhaustivité de la réponse de Jason est acceptable, étant donné qu'elle est précise par rapport à la question spécifique du PO. Cependant, je pense que votre élaboration est utile.
En regardant mon commentaire ci-dessus, j'ai remarqué que j'ai une faute de frappe... J'ai accidentellement dit "ressources de nettoyage déterministe". J'aurais dû dire "de manière non déterministe". Si je pouvais modifier le commentaire ci-dessus, je le ferais :)
El Pen
sera collecté par le GC à un moment indéterminé dans le futur, que vous appeliez ou non Dispose
.
Cependant, toute ressource non gérée détenue par le stylo (par exemple, un handle GDI+) ne sera pas nettoyée par le GC. Le GC ne nettoie que les ressources gérées. Appeler Pen.Dispose
vous permet de vous assurer que ces ressources non gérées sont nettoyées en temps voulu et que vous ne perdez pas de ressources.
Maintenant, si le Pen
a un finalisateur et que ce dernier nettoie les ressources non gérées, alors ces ressources non gérées seront nettoyées lorsque l'application Pen
est collecté par les ordures. Mais le fait est que :
Dispose
explicitement pour que vous libériez vos ressources non gérées, etPen
met en œuvre IDisposable
. IDisposable
sert à disposer des ressources non gérées. C'est le modèle utilisé dans .NET.
Pour les commentaires précédents sur ce sujet, veuillez consulter ce qui suit réponse .
Cette réponse est incomplète et légèrement trompeuse. J'ai expliqué pourquoi dans mon message ci-dessous.
-1 pour l'affirmation selon laquelle le GC ne nettoiera pas les ressources non gérées. Si l'implémentation d'IDisposable fait cela correctement, elle fonctionnera très bien depuis le fil de finalisation, mais un peu plus tard.
Le GC ne libère pas la mémoire ou les ressources non gérées, quelle que soit la façon dont Dispose() est implémenté. Ce qu'il peut est de vous permettre d'appeler Marshal.ReleaseComObject() et d'implémenter un Finalizer qui sera appelé si GC.SuppressFinalize() n'est pas appelé. Ce n'est pas parce que le nettoyage de la mémoire non gérée a lieu pendant la finalisation ou dans l'implémentation de Dispose que le GC est celui qui effectue la libération. Le GC n'est pas responsable du nettoyage des ressources non gérées. Si le programmeur ne le fait pas, la mémoire sera perdue.
Le gestionnaire de stylo GDI+ sous-jacent ne sera pas libéré avant un moment indéterminé dans le futur, c'est-à-dire lorsque l'objet stylo sera récupéré et que le finalisateur de l'objet sera appelé. Il se peut que ce ne soit pas avant la fin du processus, ou plus tôt, mais l'essentiel est que ce soit indéterminé. L'appel à Dispose vous permet de faire un nettoyage déterministe et est fortement recommandé.
Si vous voulez vraiment savoir ce qu'il en est lorsque vous n'appelez pas Dispose sur les objets graphiques, vous pouvez utiliser le CLR Profiler, disponible gratuitement au téléchargement. ici. Dans le dossier d'installation (par défaut C:\CLRProfiler ) est CLRProfiler.doc qui contient un bel exemple de ce qui se passe lorsque vous n'appelez pas Dispose sur un objet Brush. C'est très instructif. La version courte est que les objets graphiques occupent une plus grande partie de la mémoire que vous ne le pensez et qu'ils peuvent rester en place pendant longtemps si vous n'appelez pas Dispose sur eux. Une fois que les objets ne sont plus utilisés, le système finit par les nettoyer, mais ce processus prend plus de temps au processeur que si vous aviez simplement appelé Dispose lorsque vous en aviez fini avec les objets. Vous pouvez également vous renseigner sur l'utilisation d'IDisposable. aquí y aquí .
La quantité totale de mémoire .Net utilisée est la partie .Net + toutes les données "externes" utilisées. Les objets du système d'exploitation, les fichiers ouverts, les bases de données et les connexions réseau utilisent tous des ressources qui ne sont pas purement des objets .Net.
Les graphiques utilisent des stylos et d'autres objets qui sont en fait des objets OS dont la conservation est "assez" coûteuse. (Vous pouvez échanger votre stylo contre un fichier bitmap 1000x1000). Ces objets OS ne sont supprimés de la mémoire OS que lorsque vous appelez une fonction de nettoyage spécifique. Les fonctions Pen et Bitmap Dispose le font pour vous immédiatement lorsque vous les appelez.
Si vous n'appelez pas Dispose, le ramasseur d'ordures viendra les nettoyer " quelque part dans le futur* ". (Il appellera en fait le code de destruction/finalisation qui appelle probablement Dispose()).
*sur une machine avec une mémoire infinie (ou plus de 1 Go), quelque part dans le futur peut être très loin dans le futur. Sur une machine qui ne fait rien, il faut facilement plus de 30 minutes pour nettoyer un énorme bitmap ou un très petit stylo.
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.
36 votes
Rien. C'est pourquoi tu devrais l'appeler.
0 votes
Il me semble que la documentation MSDN sur ce que fait la méthode est assez claire. msdn.microsoft.com/fr/us/library/
0 votes
Eh bien, je suppose que j'aurais pu formuler la question comme suit . n'a pas si je n'appelle pas
Dispose
mais je pense que vous comprenez ce que je veux dire ?0 votes
Une meilleure question est de savoir pourquoi vous ne voulez pas appeler Dispose() ? Si j'écrivais ceci, je le mettrais dans une déclaration using.
0 votes
@Bryan Je sais que je suis censé appeler Dispose, j'essaie de comprendre pourquoi. S'il est si important que je l'appelle, pourquoi ne le fait-on pas automatiquement ?
7 votes
.net ne fait pas d'analyse d'échappement. Il ne sait donc pas si la référence survit après l'exécution de
pen
sort du champ d'application. Ainsi, vous devez attendre que la GC décide de collecter le fichierPen
qui pourrait être beaucoup plus tard.0 votes
Et pour les objets qui font partie d'un plus grand graphe d'objets (ce n'est pas le cas pour les
Pen
) l'objet peut être maintenu en vie par certaines références qui ne sont supprimées que si vous disposez de l'objet.0 votes
@Andreas, " ... pourquoi cela ne se fait-il pas automatiquement ? " Lorsque l'appellerait-il "automatiquement" ? ?! Comment le framework pourrait-il déterminer quand l'appeler... c'est à vous, le programmeur... et c'est pour cela que les concepteurs du langage ont ajouté l'option
using
pour vous permettre de spécifier quand l'appeler sans avoir à écrire explicitement un appel à celui-ci...0 votes
Andreas Brinck : Je sais que ça ne répond pas à votre question. C'est pourquoi ce n'était pas une réponse mais un jeu de mots. dans un commentaire . Bien que je pense que le terme approprié soit "truisme" plutôt que "sarcasme". Ou, peut-être, "sarcasme" - comme dans ce commentaire ;)
1 votes
Si vous êtes trop paresseux pour taper
myobj.Dispose()
vous devez entourer votre code d'un [using
déclaration]( msdn.microsoft.com/fr/us/library/yh598w02(v=VS.80).aspx)) .0 votes
Ce n'est pas seulement de la paresse car il faudrait utiliser
try
...finally
pour la sécurité des exceptions et le code est plus difficile à lire qu'en utilisantusing
.0 votes
Va en prison. Allez directement en prison. Ne passe pas, ne récupère pas 200 $.
0 votes
Le problème est que parfois vous ne pouvez pas utiliser "using", par exemple lorsque votre classe doit contenir des champs de type non géré.