Question

Je suis fasciné par la façon dont le CLR et GC fonctionne (je travaille sur l'élargissement de mes connaissances sur ce en lisant CLR via C #, livres / messages de Jon Skeet et plus).

De toute façon, quelle est la différence entre dire:

MyClass myclass = new MyClass();
myclass = null;

Ou, en faisant MyClass mettre en œuvre IDisposable et un destructor et appeler Dispose ()?

En outre, si j'ai un bloc de code avec une instruction en utilisant (par exemple ci-dessous), si je fais un pas par le code et la sortie du bloc à l'aide, est disposé l'objet de l'époque ou lorsqu'une collecte des ordures se produit? Que se passerait-il si je l'appelle Dispose () dans le bloc en utilisant anyay?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

Les classes de flux (par exemple BinaryWriter) ont une méthode Finaliser? Pourquoi voudrais-je l'utiliser?

Était-ce utile?

La solution

Il est important de disposition distincte de la collecte des ordures. Ce sont des choses complètement séparées, avec un point commun que je viendrai dans une minute.

Dispose, la collecte des ordures et la finalisation

Lorsque vous écrivez une déclaration de using, il est tout simplement le sucre syntaxique pour un bloc try / finally pour que Dispose est appelé, même si le code dans le corps de la déclaration de using lève une exception. Il ne pas signifie que l'objet est nettoyé à la fin du bloc.

Mise au rebut est sur le ressources non gérés (ressources non mémoire). Celles-ci pourraient être poignées interface utilisateur, les connexions réseau, les descripteurs de fichiers, etc. Ce sont des ressources limitées, de sorte que vous voulez généralement de les libérer dès que possible. Vous devez mettre en œuvre chaque fois que IDisposable votre type "possède" une ressource non gérée, soit directement (généralement via un IntPtr) ou indirectement (par exemple au moyen d'un Stream, un SqlConnection etc).

Collecte des ordures ménagères est lui-même que sur la mémoire - avec un petit twist. Le garbage collector est capable de trouver des objets qui ne peuvent plus être référencés, et les libérer. Il ne semble pas pour les ordures tout le temps cependant - que lorsqu'il détecte qu'il doit (par exemple si une « génération » du tas court de mémoire)

.

La torsion est finalisation . Le garbage collector conserve une liste d'objets qui ne sont plus accessibles, mais qui ont un finaliseur (écrit ~Foo() en C #, un peu confusément - ils sont rien comme les C Destructeurs de). Il exécute les finaliseurs sur ces objets, juste au cas où ils ont besoin pour faire un nettoyage supplémentaire avant leur mémoire est libérée.

Finaliseurs sont presque toujours utilisés pour nettoyer les ressources dans le cas où l'utilisateur du type a oublié d'en disposer d'une manière ordonnée. Donc, si vous ouvrez un FileStream mais oubliez d'appeler Dispose ou Close, le finaliseur éventuellement libérer la poignée de fichier sous-jacent pour vous. Dans un programme bien écrit, finaliseurs devraient tirer presque jamais à mon avis.

Définition d'une variable à null

Un petit point sur la définition d'une variable null - c'est presque jamais nécessaire pour le bien de la collecte des ordures. Vous voudrez peut-être parfois de le faire si elle est une variable membre, bien que dans mon expérience, il est rare de « partie » d'un objet à ne plus être nécessaire. Quand il est une variable locale, le JIT est généralement assez intelligent (en mode de libération) pour savoir quand vous ne comptez pas utiliser à nouveau une référence. Par exemple:

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

La seule fois où il peut être une valeur définissant une variable locale à null est quand vous êtes dans une boucle, et certaines branches de la boucle besoin d'utiliser la variable mais vous savez que vous avez atteint un point où vous ne le faites pas. Par exemple:

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

Mise en œuvre IDisposable / finalizers

Alors, si vos propres types mettre en œuvre finalizers? Presque certainement pas. Si vous ne indirectement détenir des ressources non gérés (par exemple, vous avez un FileStream comme une variable membre) puis en ajoutant votre propre finaliseur ne sera pas utile: le flux sera presque certainement admissible à la collecte des ordures lorsque votre objet est, de sorte que vous pouvez simplement compter sur FileStream ayant un finaliseur (si nécessaire - il peut se référer à autre chose, etc.). Si vous souhaitez conserver une ressource non gérée « presque » directement, SafeHandle est votre ami - il faut un peu de temps pour aller avec, mais cela signifie que vous aurez presque jamais besoin d'écrire un finaliseur à nouveau. Vous devez en général besoin d'un finaliseur si vous avez une poignée vraiment directe sur une ressource (un IntPtr) et vous devriez chercher à passer à SafeHandle dès que vous le pouvez. (Il y a deux liens là-bas - lire à la fois, idéalement.)

Joe Duffy a ensemble très longue des lignes directrices sur finaliseurs et IDisposable (co-écrit avec beaucoup de gens intelligents) qui méritent d'être lus. Il vaut la peine d'être conscient que si vous scellez vos classes, il rend la vie beaucoup plus facile. Le modèle de Dispose primordial d'appeler une nouvelle méthode de Dispose(bool) virtuelle etc est pertinent que lorsque votre classe est conçue pour l'héritage

Cela a été un peu une randonnée, mais s'il vous plaît demander des éclaircissements où vous souhaitez quelques-uns:)

Autres conseils

Lorsque vous disposez d'un objet, les ressources sont libérées. Lorsque vous attribuez null à une variable, vous êtes juste changer une référence.

myclass = null;

Après avoir exécuté cela, le maclasse objet se référait existe encore et continuera à jusqu'à ce que le GC se déplace pour le nettoyer. Si Dispose est appelé explicitement, ou il est dans un bloc à l'aide, les ressources seront libérés le plus rapidement possible.

Les deux opérations ne sont pas grand-chose à voir avec les uns des autres. Lorsque vous définissez une référence null, il ne suffit que. Il ne modifie pas en soi la classe qui a été fait référence à tous. Votre variable ne fait pas de points plus à l'objet qu'il utilise, mais l'objet lui-même ne change pas.

Lorsque vous appelez Dispose (), il est un appel de méthode sur l'objet lui-même. Quelle que soit la méthode Dispose fait, est maintenant fait sur l'objet. Mais cela ne touche pas votre référence à l'objet.

La seule zone de chevauchement est que lorsque il n'y a plus de références à un objet, il éventuellement se ramasse-miettes. Et si la classe implémente l'interface IDisposable, puis Dispose () sera appelée sur l'objet avant qu'il ne soit ramasse-miettes.

Mais cela ne se produira pas immédiatement après avoir défini votre référence null, pour deux raisons. Tout d'abord, d'autres références peuvent exister, donc il ne sera pas les déchets collectés tout encore, et deuxièmement, même si cela était la dernière référence, il est maintenant prêt à être déchets collectés, rien ne se passera jusqu'à ce que le garbage collector décide de supprimer l'objet.

L'appel Dispose () sur un objet ne pas « tuer » l'objet d'aucune façon. Il est couramment utilisé pour nettoyer afin que l'objet peut être supprimés en toute sécurité après, mais en fin de compte, il n'y a rien de magique Dispose, il est juste une méthode de classe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top