2008-08-05 4 views
170

Si vous définissez tous les objets sur (Nothing dans VB.NET) une fois que vous en avez terminé avec eux? Je comprends que dans .NET il est essentiel de disposer de toutes les instances d'objets qui implémentent l'interface IDisposable pour libérer certaines ressources bien que l'objet puisse toujours être quelque chose après qu'il soit disposé (d'où la propriété isDisposed dans les formulaires), donc Je suppose qu'il peut encore résider en mémoire ou au moins en partie?Définition des objets sur Null/Nothing après l'utilisation dans .NET

Je sais aussi que lorsqu'un objet sort de la portée, il est marqué comme prêt pour la prochaine passe du garbage collector (bien que cela puisse prendre du temps).

Donc, dans cet esprit, le réglage à null accélérera le système libérant la mémoire car il n'a pas à fonctionner qu'il n'est plus dans la portée et sont-ils des effets secondaires négatifs? Les articles MSDN ne le font jamais dans les exemples et actuellement je le fais car je ne peux pas voir le mal. Cependant, j'ai rencontré un mélange d'opinions, donc tous les commentaires sont utiles.

+3

+1 bonne question. Est-ce que quelqu'un sait une circonstance sous laquelle le compilateur optimisera complètement la cession? c'est-à-dire que quelqu'un a regardé MSIL dans des circonstances différentes et a noté IL pour mettre un objet à zéro (ou l'absence de celui-ci). –

Répondre

66

Karl a absolument raison, il n'est pas nécessaire de définir des objets sur null après utilisation. Si un objet implémente IDisposable, assurez-vous d'appeler IDisposable.Dispose() lorsque vous avez terminé avec cet objet (enveloppé dans un try .. finally ou un bloc using()). Mais même si vous ne vous souvenez pas d'appeler Dispose(), la méthode de finaliser sur l'objet devrait appeler Dispose() pour vous.

Je pensais que c'était un bon traitement:

Digging into IDisposable

et ce

Understanding IDisposable

Il n'y a pas d'essayer de deviner la GC et ses stratégies de gestion parce que c'est auto-tuning et opaque. Il y avait une bonne discussion sur le fonctionnement interne avec Jeffrey Richter sur Rocks Dot Net ici: Jeffrey Richter on the Windows Memory Model et Richters livre CLR via C# chapitre 20 a un traitement:

+5

La règle de ne pas mettre à null n'est pas "dure et rapide" ... si l'objet est placé sur le tas d'objets volumineux (taille> 85K) cela aidera le GC si vous mettez l'objet à zéro quand vous êtes fait en l'utilisant. –

+0

Je suis d'accord dans une certaine mesure, mais à moins que vous commenciez à ressentir de la pression mémoire, je ne vois pas la nécessité de «prématurément optimiser» en réglant les objets à zéro après utilisation. – Kev

+18

Toute cette affaire de "ne pas prématurément optimiser" ressemble plus à "Préférez lent et ne vous inquiétez pas parce que les processeurs sont plus rapides et les applications CRUD n'ont pas besoin de vitesse de toute façon." C'est peut-être juste moi. :) – BobbyShaftoe

-1

Certains objets supposent la méthode .dispose() qui force la ressource à être supprimée de la mémoire.

+10

Non, ce n'est pas le cas; Dispose() ne collecte * pas * l'objet - il est utilisé pour effectuer un nettoyage déterministe, libérant généralement des ressources non gérées. –

+1

Gardez à l'esprit que le déterminisme s'applique uniquement aux ressources gérées, pas aux ressources non gérées (mémoire) – nicodemus13

4

La seule heure à laquelle vous devez définir une variable sur null est lorsque la variable n'est pas hors de portée et que vous n'avez plus besoin des données qui lui sont associées. Sinon, il n'y a pas besoin.

+2

C'est vrai, mais cela signifie également que vous devriez probablement refactoriser votre code. Je ne pense pas avoir jamais eu besoin de déclarer une variable en dehors de sa portée prévue. –

+1

Si l'on comprend que "variable" inclut des champs d'objet, alors cette réponse a beaucoup de sens. Dans le cas où "variable" signifie seulement "variable locale" (d'une méthode), alors nous parlons probablement de cas de niche ici (par exemple une méthode qui dure beaucoup plus longtemps que d'habitude). – stakx

7

aussi:

using(SomeObject object = new SomeObject()) 
{ 
    // do stuff with the object 
} 
// the object will be disposed of 
1

Il y a des cas où il est logique de références null. Par exemple, lorsque vous écrivez une collection - comme une file d'attente prioritaire - et par votre contrat, vous ne devriez pas garder ces objets en vie pour le client après que le client les ait retirés de la file d'attente.

Mais ce genre de chose n'a d'importance que dans les collections à vie longue. Si la file d'attente ne survit pas à la fin de la fonction dans laquelle elle a été créée, alors cela importe beaucoup moins.

Dans l'ensemble, vous ne devriez vraiment pas déranger. Laissez le compilateur et le GC faire leur travail pour que vous puissiez faire le vôtre.

8

En général, il n'y a pas besoin de null objets après utilisation, mais dans certains cas, je trouve que c'est une bonne pratique.

Si un objet implémente IDisposable et est stocké dans un champ, je pense qu'il est bon de l'annuler, juste pour éviter d'utiliser l'objet disposé. Les insectes du genre suivant peuvent être douloureux:

this.myField.Dispose(); 
// ... at some later time 
this.myField.DoSomething(); 

Il est bon de null le terrain après l'élimination, et obtenir un NullPtrEx droit à la ligne où le champ est utilisé à nouveau. Sinon, vous pourriez tomber sur un bug énigmatique (selon ce que DoSomething fait exactement).

+8

Eh bien, un objet disposé devrait lancer ObjectDisposedException s'il a déjà été éliminé. Pour autant que je sache, cela nécessite un code général, mais là encore, Disposed est un paradigme mal conçu de toute façon. – nicodemus13

+2

Ctrl + F pour '.Dispose()'. Si vous le trouvez, vous n'utilisez pas IDisposable correctement. La seule utilisation pour un objet jetable devrait être dans les limites d'un bloc d'utilisation. Et après le bloc using, vous n'avez même plus accès à 'myField'. Et dans le bloc using, la définition de 'null' n'est pas nécessaire, le bloc using disposera l'objet pour vous. – Suamere

6

Les chances sont que votre code n'est pas suffisamment structuré si vous ressentez le besoin de variables.

Il y a plusieurs façons de limiter la portée d'une variable:

Comme mentionné par Steve Tranby

using(SomeObject object = new SomeObject()) 
{ 
    // do stuff with the object 
} 
// the object will be disposed of 

De même, vous pouvez simplement utiliser des accolades:

{ 
    // Declare the variable and use it 
    SomeObject object = new SomeObject() 
} 
// The variable is no longer available 

Je trouve que l'utilisation de parenthèses bouclés sans "titre" pour vraiment nettoyer le code et aider à le rendre plus compréhensible.

+0

J'ai essayé d'utiliser des portées locales personnalisées une fois (la plupart étant un smarta $$). L'entreprise a explosé. – Suamere

+0

Sur une autre note: Ceci parce que le compilateur C# trouvera des variables à portée locale qui implémentent IDisposable, et appellera .Dispose (MOST Of the time) lorsque leur portée se terminera. Cependant ... Les connexions SQL sont un grand moment quand .Dispose() n'est jamais optimisé-in. Il y a des types qui nécessitent une attention explicite, donc personnellement, je fais toujours les choses explicitement juste pour ne pas me faire mordre. – Suamere

33

Une autre raison pour éviter de définir des objets sur null lorsque vous en avez terminé est qu'ils peuvent les garder en vie plus longtemps.

par exemple.

void foo() 
{ 
    var someType = new SomeType(); 
    someType.DoSomething(); 
    // someType is now eligible for garbage collection   

    // ... rest of method not using 'someType' ... 
} 

permettra à l'objet visé par UnType à GC'd après l'appel à « DoSomething » mais

void foo() 
{ 
    var someType = new SomeType(); 
    someType.DoSomething(); 
    // someType is NOT eligible for garbage collection yet 
    // because that variable is used at the end of the method   

    // ... rest of method not using 'someType' ... 
    someType = null; 
} 

peut parfois garder l'objet en vie jusqu'à la fin de la méthode. Le JIT will usually optimized away the assignment to null, donc les deux bits de code finissent par être les mêmes.

+0

C'est un point intéressant. J'ai toujours pensé que les objets ne sortent pas de la portée tant que la méthode dans laquelle ils sont définis n'est pas complète. À moins bien sûr que l'objet soit défini dans un bloc Using ou soit explicitement défini sur Nothing ou null. –

+1

Le meilleur moyen de s'assurer qu'ils restent en vie est d'utiliser 'GC.KeepAlive (someType);' Voir http://ericlippert.com/2013/06/10/construction-destruction/ – NotMe

1

Jetez un oeil à cet article ainsi: http://www.codeproject.com/KB/cs/idisposable.aspx

Pour la plupart, la fixation d'un objet à nul n'a aucun effet. La seule fois où vous devriez être sûr de le faire est si vous travaillez avec un "grand objet", qui est une taille supérieure à 84K (comme les bitmaps).

3

ce type de «il n'est pas nécessaire de définir des objets à zéro après utilisation» n'est pas entièrement précis. Il y a des fois où vous devez NULL la variable après l'avoir mise au rebut.

Oui, vous devez TOUJOURS appeler le .Dispose() ou .Close() sur tout ce qui l'a lorsque vous avez terminé. Qu'il s'agisse de poignées de fichiers, de connexions à une base de données ou d'objets jetables. Le modèle très pratique de LazyLoad est séparé de celui-ci. Dire que j'ai et instancié ObjA de class A. Class A a une propriété publique appelée PropB de class B.

En interne, PropB utilise la variable privée _B et la valeur par défaut est null. Lorsque PropB.Get() est utilisé, il vérifie si _PropB est nul et, dans ce cas, ouvre les ressources nécessaires pour instancier un B en _PropB. Il renvoie ensuite _PropB. Selon mon expérience, c'est une astuce très utile.

Lorsque le besoin null est disponible en est si vous rétablissez ou modification d'une certaine façon que le contenu de _PropB étaient l'enfant des valeurs précédentes de A, vous devez vous débarrasser et Null sur _PropB si LazyLoad peut réinitialiser récupère la bonne valeur SI le code l'exige.

Si vous ne faites que _PropB.Dispose() et que peu de temps après la vérification null pour que LazyLoad réussisse, il ne sera pas nul, et vous regarderez des données périmées. En effet, vous devez l'annuler après Dispose() juste pour être sûr.

Je voudrais bien qu'il en fût autrement, mais j'ai le code obtenu droit présentant maintenant ce comportement après une Dispose() sur un _PropB et à l'extérieur de la fonction d'appel qui a fait l'Dispose (et donc presque hors de portée), l'hélice privée n'est toujours pas nul, et les données périmées sont toujours là.

Finalement, la propriété éliminée sera nulle, mais cela a été non déterministe de mon point de vue.

La raison principale, comme dbkk fait allusion est que le conteneur parent (ObjA avec PropB) maintient l'instance de _PropB portée, en dépit de la Dispose().

+0

Bon exemple montrant comment mettre à zéro manuellement signifie une erreur plus fatale pour l'appelant, ce qui est une bonne chose. – rolls

5

En général, pas besoin de définir à null. Mais supposons que vous ayez une fonctionnalité de réinitialisation dans votre classe.

Ensuite, vous pourriez le faire, car vous ne voulez pas appeler l'élimination deux fois, car une partie de la mise au rebut peut ne pas être implémentée correctement et lancer l'exception System.ObjectDisposed.

private void Reset() 
{ 
    if(_dataset != null) 
    { 
     _dataset.Dispose(); 
     _dataset = null; 
    } 
    //..More such member variables like oracle connection etc. _oraConnection 
} 
0

je crois par la conception des GC implementors, vous ne pouvez pas accélérer GC avec l'annulation. Je suis sûr qu'ils préféreraient que vous ne vous inquiétiez pas avec comment/quand le GC fonctionne - traitez-le comme ceci omniprésent Etre en protégeant et en veillant sur vous ... (les arcs la tête en bas, lève le poing vers le ciel) ...

Personnellement, je définis souvent explicitement les variables comme nulles quand j'en ai fini avec elles comme une forme de documentation autonome. Je ne déclare pas, n'utilise pas, puis mets à null plus tard - je les annule immédiatement après qu'ils ne sont plus nécessaires. Je dis, explicitement, "j'en ai officiellement fini avec vous ... être parti ..."

Est-il nécessaire d'annuler dans une langue GC'd? Non. Est-ce utile pour le GC? Peut-être que oui, peut-être non, je ne sais pas, par conception, je ne peux vraiment pas le contrôler, et indépendamment de la réponse d'aujourd'hui avec cette version ou cela, les futures implémentations GC pourraient changer la réponse au-delà de mon contrôle. De plus, si/quand l'annulation est optimisée, c'est un peu plus qu'un simple commentaire si vous voulez. Je pense que si cela rend mon intention plus claire pour le prochain imbécile qui suit mes traces, et si cela peut potentiellement aider le GC parfois, alors ça vaut le coup pour moi. La plupart du temps, je me sens bien rangé et clair, et Mongo aime se sentir bien rangé et clair. :)

Je le vois comme ceci: Les langages de programmation existent pour permettre aux gens de donner aux autres une idée de l'intention et un compilateur une demande de travail pour quoi faire - le compilateur convertit cette requête dans une langue différente (parfois plusieurs) pour un CPU - le CPU (s) pourrait donner un hoot quelle langue vous avez utilisé, vos paramètres de tabulation, commentaires, accents stylistiques, noms de variables, etc - un CPU tout sur le flux de bits qui lui dit ce registres et opcodes et les emplacements de mémoire à tourner. Beaucoup de choses écrites en code ne se convertissent pas en ce qui est consommé par le CPU dans la séquence que nous avons spécifiée. Notre C, C++, C#, Lisp, Babel, assembleur ou quoi que ce soit est la théorie plutôt que la réalité, écrit comme une déclaration de travail. Ce que vous voyez n'est pas ce que vous obtenez, oui, même en langage assembleur.

Je comprends l'état d'esprit des "choses inutiles" (comme les lignes vides) "ne sont rien d'autre que le bruit et le code de fouillis." C'était moi plus tôt dans ma carrière; Je comprends tout à fait cela. À ce stade, je penche vers ce qui rend le code plus clair. Ce n'est pas comme si j'ajoutais même 50 lignes de "bruit" à mes programmes - c'est quelques lignes ici ou là.

Il existe des exceptions à toute règle. Dans des scénarios avec de la mémoire volatile, de la mémoire statique, des conditions de course, des singletons, l'utilisation de données "périmées" et tout ce genre de pourriture, vous devez gérer votre propre mémoire, verrouiller et annuler à cause de la mémoire. l'univers GC'd - j'espère que tout le monde comprend cela. Le reste du temps avec les langages GC'd est une question de style plutôt que de nécessité ou de garantie de performance. À la fin de la journée, assurez-vous de comprendre ce qui est admissible au GC et ce qui ne l'est pas; verrouiller, éliminer et annuler de manière appropriée; cirer, enlever la cire; Inspire, expire; et pour tout ce que je dis: si cela fait du bien, faites-le. Votre kilométrage peut varier ... comme il se doit ...

Questions connexes