Question

Le garbage collector .NET finira par libérer de la mémoire, mais que se passe-t-il si vous souhaitez récupérer cette mémoire immédiatement ?Quel code devez-vous utiliser dans une classe MyClass appeler

MyClass.Dispose()

et libérez tout l'espace utilisé par les variables et les objets dans MyClass?

Était-ce utile?

La solution

IDisposable n'a rien à voir avec la libération de mémoire.IDisposable est un modèle pour libérer non géré ressources - et la mémoire est sans aucun doute une ressource gérée.

Les liens pointant vers GC.Collect() sont la bonne réponse, bien que l'utilisation de cette fonction soit généralement déconseillée par la documentation Microsoft .NET.

Modifier: Ayant gagné une quantité substantielle de karma pour cette réponse, je ressens une certaine responsabilité de développer cela, de peur qu'un nouveau venu dans la gestion des ressources .NET n'ait une mauvaise impression.

Dans un processus .NET, il existe deux types de ressources : gérées et non gérées.« Géré » signifie que le runtime contrôle la ressource, tandis que « non géré » signifie que c'est la responsabilité du programmeur.Et il n’existe aujourd’hui qu’un seul type de ressource gérée qui nous intéresse dans .NET : la mémoire.Le programmeur indique au moteur d'exécution d'allouer de la mémoire et c'est ensuite au moteur d'exécution de déterminer quand la mémoire peut être libérée.Le mécanisme utilisé par .NET à cette fin est appelé collecte des ordures et vous pouvez trouver de nombreuses informations sur GC sur Internet simplement en utilisant Google.

Pour les autres types de ressources, .NET ne sait rien de leur nettoyage et doit donc s'appuyer sur le programmeur pour faire ce qu'il faut.Pour cela, la plateforme met à disposition du programmeur trois outils :

  1. L'interface IDisposable et l'instruction "using" en VB et C#
  2. Finalisateurs
  3. Le modèle IDisposable tel qu'implémenté par de nombreuses classes BCL

Le premier d’entre eux permet au programmeur d’acquérir efficacement une ressource, de l’utiliser puis de la libérer selon la même méthode.

using (DisposableObject tmp = DisposableObject.AcquireResource()) {
    // Do something with tmp
}
// At this point, tmp.Dispose() will automatically have been called
// BUT, tmp may still a perfectly valid object that still takes up memory

Si "AcquireResource" est une méthode d'usine qui (par exemple) ouvre un fichier et "Dispose" ferme automatiquement le fichier, alors ce code ne peut pas divulguer une ressource de fichier.Mais la mémoire de l'objet "tmp" lui-même pourrait bien encore être allouée.C'est parce que l'interface IDisposable n'a absolument aucune connexion avec le ramasse-miettes.Si tu a fait voulez vous assurer que la mémoire a été libérée, votre seule option serait d'appeler GC.Collect() pour forcer un ramassage des ordures.

Cependant, on ne saurait trop insister sur le fait que ce n’est probablement pas une bonne idée.Il est généralement préférable de laisser le garbage collector faire ce pour quoi il a été conçu, c'est-à-dire gérer la mémoire.

Que se passe-t-il si la ressource est utilisée sur une période plus longue, de telle sorte que sa durée de vie croise plusieurs méthodes ?De toute évidence, l'instruction "using" n'est plus applicable, le programmeur devra donc appeler manuellement "Dispose" lorsqu'il aura terminé avec la ressource.Et que se passe-t-il si le programmeur oublie ?S'il n'y a pas de solution de secours, le processus ou l'ordinateur peut éventuellement manquer de la ressource qui n'est pas correctement libérée.

C'est là qu'interviennent les finaliseurs.Un finaliseur est une méthode de votre classe qui entretient une relation particulière avec le garbage collector.Le GC promet que - avant de libérer de la mémoire pour tout objet de ce type - il donnera d'abord au finaliseur une chance d'effectuer une sorte de nettoyage.

Ainsi, dans le cas d’un fichier, nous n’avons théoriquement pas du tout besoin de fermer le fichier manuellement.Nous pouvons simplement attendre que le garbage collector y parvienne, puis laisser le finaliseur faire le travail.Malheureusement, cela ne fonctionne pas bien en pratique car le garbage collector fonctionne de manière non déterministe.Le fichier peut rester ouvert beaucoup plus longtemps que prévu par le programmeur.Et si suffisamment de fichiers restent ouverts, le système peut échouer lors de la tentative d'ouverture d'un fichier supplémentaire.

Pour la plupart des ressources, nous souhaitons ces deux choses.Nous voulons qu'une convention puisse dire "nous en avons fini avec cette ressource maintenant" et nous voulons nous assurer qu'il y a au moins une chance que le nettoyage se fasse automatiquement si nous oublions de le faire manuellement.C'est là que le modèle « IDisposable » entre en jeu.Il s'agit d'une convention qui permet à IDispose et à un finaliseur de bien fonctionner ensemble.Vous pouvez voir comment le modèle fonctionne en regardant le documentation officielle pour IDisposable.

Conclusion : Si ce que vous voulez vraiment faire est simplement de vous assurer que la mémoire est libérée, alors IDisposable et les finaliseurs ne vous aideront pas.Mais l'interface IDisposable fait partie d'un modèle extrêmement important que tous les programmeurs .NET devraient comprendre.

Autres conseils

Vous ne pouvez supprimer que les instances qui implémentent l'interface IDisposable.

Pour forcer un garbage collection à libérer immédiatement la mémoire (non gérée) :

GC.Collect();  
GC.WaitForPendingFinalizers();

C'est normalement une mauvaise pratique, mais il y a par exemple un bug dans la version x64 du framework .NET qui fait que le GC se comporte étrangement dans certains scénarios, et vous voudrez peut-être le faire.Je ne sais pas si le bug a encore été résolu.Est-ce que quelqu'un sait?

Pour supprimer une classe, procédez comme suit :

instance.Dispose();

ou comme ça :

using(MyClass instance = new MyClass())
{
    // Your cool code.
}

qui se traduira au moment de la compilation par :

MyClass instance = null;    

try
{
    instance = new MyClass();        
    // Your cool code.
}
finally
{
    if(instance != null)
        instance.Dispose();
}

Vous pouvez implémenter l'interface IDisposable comme ceci :

public class MyClass : IDisposable
{
    private bool disposed;

    /// <summary>
    /// Construction
    /// </summary>
    public MyClass()
    {
    }

    /// <summary>
    /// Destructor
    /// </summary>
    ~MyClass()
    {
        this.Dispose(false);
    }

    /// <summary>
    /// The dispose method that implements IDisposable.
    /// </summary>
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// The virtual dispose method that allows
    /// classes inherithed from this one to dispose their resources.
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources here.
            }

            // Dispose unmanaged resources here.
        }

        disposed = true;
    }
}

Les réponses à cette question sont devenues plus que confuses.

Le titre pose des questions sur l'élimination, mais dit ensuite qu'ils veulent récupérer la mémoire immédiatement.

.Net est géré, ce qui signifie que lorsque vous écrivez des applications .Net, vous n'avez pas à vous soucier directement de la mémoire, le coût est que vous n'avez pas non plus de contrôle direct sur la mémoire.

.Net décide quand il est préférable de nettoyer et de libérer de la mémoire, et non vous en tant que codeur .Net.

Le Dispose est un moyen de dire à .Net que vous avez terminé quelque chose, mais cela ne libérera pas de mémoire jusqu'à ce que ce soit le meilleur moment pour le faire.

Fondamentalement, .Net récupérera la mémoire lorsque cela lui sera le plus facile - il est très efficace pour décider quand.À moins que vous n'écriviez quelque chose de très gourmand en mémoire, vous n'avez normalement pas besoin de l'annuler (c'est en partie la raison pour laquelle les jeux ne sont pas encore souvent écrits en .Net - ils nécessitent un contrôle complet)

Dans .Net, vous pouvez utiliser GC.Collect() pour le forcer immédiatement, mais c'est presque toujours une mauvaise pratique.Si .Net ne l'a pas encore nettoyé, cela signifie que ce n'est pas le moment particulièrement propice pour le faire.

GC.Collect() récupère les objets que .Net identifie comme étant terminés.Si vous n'avez pas supprimé un objet qui en a besoin, .Net peut décider de conserver cet objet.Cela signifie que GC.Collect() n'est efficace que si vous implémentez correctement vos instances jetables.

GC.Collect() est pas un remplacement pour utiliser correctement IDisposable.

Donc Dispose et mémoire ne sont pas directement liés, mais ils n’ont pas besoin de l’être.Une élimination correcte rendra vos applications .Net plus efficaces et utilisera donc moins de mémoire.


99 % du temps, dans .Net, les bonnes pratiques suivantes sont :

Règle 1: Si tu ne gères rien non géré ou qui met en œuvre IDisposable alors ne vous inquiétez pas de Dispose.

Règle 2 : Si vous disposez d'une variable locale qui implémente IDisposable, assurez-vous de la supprimer dans la portée actuelle :

//using is best practice
using( SqlConnection con = new SqlConnection("my con str" ) )
{
    //do stuff
} 

//this is what 'using' actually compiles to:
SqlConnection con = new SqlConnection("my con str" ) ;
try
{
    //do stuff
}
finally
{
    con.Dispose();
}

Règle 3 : Si une classe possède une propriété ou une variable membre qui implémente IDisposable, cette classe doit également implémenter IDisposable.Dans la méthode Dispose de cette classe, vous pouvez également supprimer vos propriétés IDisposable :

//rather basic example
public sealed MyClass :
   IDisposable
{   
    //this connection is disposable
    public SqlConnection MyConnection { get; set; }

    //make sure this gets rid of it too
    public Dispose() 
    {
        //if we still have a connection dispose it
        if( MyConnection != null )
            MyConnection.Dispose();

        //note that the connection might have already been disposed
        //always write disposals so that they can be called again
    }
}

Ce n'est pas vraiment complet, c'est pourquoi l'exemple est scellé.Les classes qui héritent devront peut-être respecter la règle suivante...

Règle 4 : Si une classe utilise un non géré ressource puis implémentez IDispose et ajouter un finaliseur.

.Net ne peut rien faire avec le non géré ressource, nous parlons donc maintenant de mémoire.Si vous ne le nettoyez pas, vous risquez une fuite de mémoire.

La méthode Dispose doit gérer à la fois géré et non géré ressources.

Le finaliseur est un dispositif de sécurité : il garantit que si quelqu'un d'autre crée une instance de votre classe et ne parvient pas à la supprimer, c'est "dangereux". non géré les ressources peuvent toujours être nettoyées par .Net.

~MyClass()
{
    //calls a protected method 
    //the false tells this method
    //not to bother with managed
    //resources
    this.Dispose(false);
}

public void Dispose()
{
    //calls the same method
    //passed true to tell it to
    //clean up managed and unmanaged 
    this.Dispose(true);

    //as dispose has been correctly
    //called we don't need the 

    //'backup' finaliser
    GC.SuppressFinalize(this);
}

Enfin cette surcharge de Dispose qui prend un flag booléen :

protected virtual void Dispose(bool disposing)
{
    //check this hasn't been called already
    //remember that Dispose can be called again
    if (!disposed)
    {
        //this is passed true in the regular Dispose
        if (disposing)
        {
            // Dispose managed resources here.
        }

        //both regular Dispose and the finaliser
        //will hit this code
        // Dispose unmanaged resources here.
    }

    disposed = true;
}

Notez qu'une fois que tout est en place, tout autre code managé créant une instance de votre classe peut simplement la traiter comme n'importe quel autre IDisposable (règles 2 et 3).

Serait-il approprié de mentionner également que disposer ne fait pas toujours référence à la mémoire ?Je dispose de ressources telles que des références à des fichiers plus souvent que de la mémoire.GC.Collect() se rapporte directement au garbage collector CLR et peut ou non libérer de la mémoire (dans le Gestionnaire des tâches).Cela aura probablement un impact négatif sur votre application (par exemple, les performances).

En fin de compte, pourquoi voulez-vous que la mémoire soit récupérée immédiatement ?S'il y a une pression sur la mémoire ailleurs, le système d'exploitation vous fournira de la mémoire dans la plupart des cas.

Regarde ça article

L'implémentation du modèle Dispose, IDisposable et/ou d'un finaliseur n'a absolument rien à voir avec le moment où la mémoire est récupérée ;au lieu de cela, cela a tout à voir avec le fait de dire au GC comment pour récupérer cette mémoire.Lorsque vous appelez Dispose(), vous n'interagissez en aucun cas avec le GC.

Le GC ne s'exécutera que lorsqu'il en déterminera le besoin (appelé pression de la mémoire), puis (et alors seulement) il libérera la mémoire pour les objets inutilisés et compactera l'espace mémoire.

Toi pourrait appelez GC.Collect() mais vous ne devriez vraiment pas le faire à moins qu'il n'y ait un très bonne raison (ce qui est presque toujours "Jamais").Lorsque vous forcez un cycle de collecte hors bande comme celui-ci, vous obligez le GC à effectuer plus de travail et, en fin de compte, vous pouvez finir par nuire aux performances de vos applications.Pendant la durée du cycle de collecte GC, votre application est en fait dans un état gelé... plus il y a de cycles GC exécutés, plus votre application passe de temps gelée.

Il existe également des appels API Win32 natifs que vous pouvez effectuer pour libérer votre ensemble de travail, mais même ceux-ci doivent être évités, sauf en cas de problème. très bonne raison de le faire.

Le principe derrière un runtime collecté par les déchets est que vous n'avez pas besoin de vous soucier (autant) du moment où le runtime alloue/libère de la mémoire réelle ;vous n'avez qu'à vous soucier de vous assurer que votre objet sait comment nettoyer après lui-même lorsqu'on vous le demande.

public class MyClass : IDisposable
{
    public void Dispose()
    {
       // cleanup here
    }
}

alors tu peux faire quelque chose comme ça

MyClass todispose = new MyClass();
todispose.Dispose(); // instance is disposed right here

ou

using (MyClass instance = new MyClass())
{

}
// instance will be disposed right here as it goes out of scope

Explication complète par Joe Duffy sur "Élimination, finalisation et gestion des ressources":

Plus tôt dans la durée de vie du Framework .NET, les finalisateurs étaient toujours appelés destructeurs par les programmeurs C #.À mesure que nous devenons plus intelligents au fil du temps, nous essayons de nous réconcilier avec le fait que le Disser la méthode est vraiment plus équivalent à un destructeur C ++ (déterministe), tandis que le Finalizer est quelque chose entièrement séparé (non-déterministe).Le fait que C # ait emprunté la syntaxe de destructrice C ++ (c'est-à-dire~ T ()) avait sûrement au moins un peu à voir avec le développement de ce terme impropre.

J'ai écrit un résumé de Destructors et Dispose and Garbage collection sur http://codingcraftsman.wordpress.com/2012/04/25/to-dispose-or-not-to-dispose/

Pour répondre à la question initiale :

  1. N'essayez pas de gérer votre mémoire
  2. Dispose ne concerne pas la gestion de la mémoire, mais la gestion des ressources non gérées
  3. Les finaliseurs font partie intégrante du modèle Dispose et ralentissent en fait la libération de mémoire des objets gérés (car ils doivent aller dans la file d'attente de finalisation à moins que Dispose d ne soit déjà fait).
  4. GC.Collect est mauvais car il donne l'impression que certains objets de courte durée sont nécessaires plus longtemps et ralentit ainsi leur collecte.

Cependant, GC.Collect pourrait être utile si vous disposiez d’une section de code critique en termes de performances et souhaitiez réduire le risque de ralentissement de la récupération de place.Vous avez déjà appelé ça.

En plus de cela, il existe un argument en faveur de ce modèle :

var myBigObject = new MyBigObject(1);
// something happens
myBigObject = new MyBigObject(2);
// at the above line, there are temporarily two big objects in memory and neither can be collected

contre

myBigObject = null; // so it could now be collected
myBigObject = new MyBigObject(2);

Mais la réponse principale est que le Garbage Collection fonctionne à moins que vous ne le manipuliez !

Vous ne pouvez pas vraiment forcer un GC à nettoyer un objet quand vous le souhaitez, bien qu'il existe des moyens de le forcer à s'exécuter, rien ne dit qu'il nettoie tous les objets que vous voulez/attendez.Il est préférable d'appeler dispose de la manière try catch ex finalement disposer end try (VB.NET rulz).Mais Dispose sert à nettoyer les ressources système (mémoire, handles, connexions à la base de données, etc.alloué par l’objet de manière déterministe.Dispose ne nettoie pas (et ne peut pas) nettoyer la mémoire utilisée par l'objet lui-même, seul le GC peut le faire.

Cet article a une procédure pas à pas assez simple.Cependant, ayant appeler le GC au lieu de le laisser suivre son cours naturel est généralement le signe d'une mauvaise conception/gestion de la mémoire, en particulier si aucune ressource limitée n'est consommée (connexions, descripteurs, tout ce qui conduit généralement à l'implémentation d'IDisposable).

Qu'est-ce qui vous pousse à faire ça ?

Désolé mais la réponse sélectionnée ici est incorrecte.Comme quelques personnes l'ont déclaré par la suite, Dispose et l'implémentation d'IDisposable n'ont rien à voir avec la libération de la mémoire associée à une classe .NET.Il est principalement et traditionnellement utilisé pour libérer des ressources non gérées telles que des descripteurs de fichiers, etc.

Bien que votre application puisse appeler GC.Collect() pour tenter de forcer une collecte par le garbage collector, cela n'aura réellement un effet que sur les éléments qui se trouvent au niveau de génération correct dans la file d'attente accessible.Il est donc possible que si vous avez effacé toutes les références à l'objet, il faudra encore quelques appels à GC.Collect() avant que la mémoire réelle ne soit libérée.

Vous ne dites pas dans votre question POURQUOI vous ressentez le besoin de libérer de la mémoire immédiatement.Je comprends que parfois il peut y avoir des circonstances inhabituelles, mais sérieusement, dans le code managé, il est presque toujours préférable de laisser le runtime s'occuper de la gestion de la mémoire.

Probablement le meilleur conseil si vous pensez que votre code utilise la mémoire plus rapidement que le GC ne la libère, vous devriez alors revoir votre code pour vous assurer qu'aucun objet qui n'est plus nécessaire n'est référencé dans les structures de données que vous avez dans les membres statiques, etc. .Essayez également d'éviter les situations dans lesquelles vous disposez de références d'objets circulaires, car il est possible que celles-ci ne soient pas non plus libérées.

@Keith,

Je suis d'accord avec toutes vos règles sauf #4.L'ajout d'un finaliseur ne doit être effectué que dans des circonstances très spécifiques.Si une classe utilise des ressources non gérées, celles-ci doivent être nettoyées dans votre fonction Dispose(bool).Cette même fonction ne doit nettoyer les ressources gérées que lorsque bool est vrai.L'ajout d'un finaliseur ajoute un coût de complexité à l'utilisation de votre objet car chaque fois que vous créez une nouvelle instance, elle doit également être placée dans la file d'attente de finalisation, qui est vérifiée à chaque fois que le GC exécute un cycle de collecte.En fait, cela signifie que votre objet survit à un cycle/génération de plus qu'il ne le devrait afin que le finaliseur puisse être exécuté.Le finaliseur ne doit pas être considéré comme un « filet de sécurité ».

Le GC n'exécutera un cycle de collecte que lorsqu'il déterminera qu'il n'y a pas suffisamment de mémoire disponible dans le tas Gen0 pour effectuer la prochaine allocation, à moins que vous ne l'aidiez en appelant GC.Collect() pour forcer une collecte hors bande. .

L'essentiel est que, quoi qu'il arrive, le GC ne sait comment libérer des ressources qu'en appelant la méthode Dispose (et éventuellement le finaliseur s'il est implémenté).Il appartient à cette méthode de « faire ce qu'il faut » et de nettoyer toutes les ressources non gérées utilisées et de demander à toutes les autres ressources gérées d'appeler leur méthode Dispose.Il est très efficace dans ce qu'il fait et peut s'auto-optimiser dans une large mesure tant qu'il n'est pas aidé par des cycles de collecte hors bande.Cela étant dit, à moins d'appeler explicitement GC.Collect, vous n'avez aucun contrôle sur le moment et dans quel ordre les objets seront supprimés et la mémoire libérée.

Si MyClass implémente IDisposable, vous pouvez faire exactement cela.

MyClass.Dispose();

La meilleure pratique en C# est la suivante :

using( MyClass x = new MyClass() ) {
    //do stuff
}

Comme cela termine la suppression dans un essai final et garantit qu'elle n'est jamais manquée.

Si vous ne voulez pas (ou ne pouvez pas) implémenter IDisposable sur votre classe, vous pouvez forcer le garbage collection comme ceci (mais c'est lent) -

GC.Collect();

L'interface IDisposable est réellement destinée aux classes contenant des ressources non gérées.Si votre classe ne contient pas de ressources non gérées, pourquoi besoin libérer des ressources avant que le ramasse-miettes ne le fasse ?Sinon, assurez-vous simplement que votre objet est instancié le plus tard possible et qu'il est hors de portée dès que possible.

Vous pouvez avoir une destruction d'objets déterministe en C++

Vous ne voulez jamais appeler GC.Collect, cela perturbe l'auto-réglage du garbage collector pour détecter la pression de la mémoire et, dans certains cas, ne fait rien d'autre qu'augmenter la génération actuelle de chaque objet sur le tas.

Pour ceux qui publient des réponses IDisposable.L’appel d’une méthode Dispose ne détruit pas un objet comme le décrit le demandeur.

@Keith :

IDisposable est destiné aux ressources gérées.

Les finaliseurs sont destinés aux ressources non gérées.

Désolé mais c'est tout simplement faux.Normalement, le finaliseur ne fait rien du tout.Cependant, si le modèle de disposition a été correctement implémenté, le finaliseur tente d'invoquer Dispose.

Dispose a deux métiers :

  • Ressources gratuites non gérées, et
  • ressources gérées imbriquées gratuites.

Et ici, votre déclaration entre en jeu car il est vrai que lors de la finalisation, un objet ne doit jamais essayer de libérer des ressources gérées imbriquées car celles-ci peuvent déjà avoir été libérées.Il doit néanmoins libérer des ressources non gérées.

Pourtant, les finaliseurs n'ont pas d'autre travail que d'appeler Dispose et dites-lui de ne pas toucher aux objets gérés. Dispose, lorsqu'il est appelé manuellement (ou via Using), doit libérer toutes les ressources non gérées et passer le Dispose message sur les objets imbriqués (et les méthodes de classe de base), mais cela jamais libérer toute mémoire (gérée).

Konrad Rudolph – ouais, normalement le finaliseur ne fait rien du tout.Vous ne devriez pas l'implémenter sauf si vous avez affaire à des ressources non gérées.

Ensuite, lorsque vous l'implémentez, vous utilisez Modèle de suppression de Microsoft (comme déjà décrit)

  • public Dispose() appels protected Dispose(true) - traite à la fois des ressources gérées et non gérées.Appel Dispose() devrait supprimer la finalisation.

  • ~Finalize appels protected Dispose(false) - traite uniquement des ressources non gérées.Cela évite les fuites de mémoire non gérées si vous ne parvenez pas à appeler le public Dispose()

~Finalize est lent et ne doit pas être utilisé sauf si vous disposez de ressources non gérées à gérer.

Les ressources gérées ne peuvent pas subir de fuite de mémoire, elles ne peuvent que gaspiller des ressources pour l'application actuelle et ralentir son garbage collection.Les ressources non gérées peuvent fuir, et ~Finalize est la meilleure pratique pour s’assurer qu’ils ne le font pas.

Dans tous les cas using est la meilleure pratique.

@Curt Hagenlocher - c'est à l'envers.Je ne comprends pas pourquoi tant de personnes ont voté alors que c'est faux.

IDisposable est pour géré ressources.

Les finaliseurs sont pour non géré ressources.

Tant que vous utilisez uniquement des ressources gérées, @Jon Limjap et moi-même avons tout à fait raison.

Pour les classes qui utilisent des ressources non gérées (et gardez à l’esprit que la grande majorité des classes .Net ne le font pas), la réponse de Patrik est complète et constitue une bonne pratique.

Évitez d'utiliser GC.Collect - c'est un moyen lent de gérer les ressources gérées et ne fait rien avec les ressources non gérées à moins que vous n'ayez correctement construit vos ~Finalizers.


J'ai supprimé le commentaire du modérateur de la question d'origine conformément à https://stackoverflow.com/questions/14593/etiquette-for-modifying-posts

En réponse à la question initiale, avec les informations fournies jusqu'à présent par l'affiche originale, il est certain à 100 % qu'il n'en sait pas suffisamment sur la programmation en .NET pour même recevoir la réponse :utilisez GC.Collect().Je dirais qu'il est probable à 99,99 % qu'il n'a vraiment pas besoin d'utiliser GC.Collect(), comme l'ont souligné la plupart des affiches.

La bonne réponse se résume à « Laissons le GC faire son travail ».Période.Vous avez d'autres choses à vous soucier.Mais vous voudrez peut-être vous demander si et quand vous devez vous débarrasser ou nettoyer des objets spécifiques, et si vous devez implémenter IDisposable et éventuellement Finalize dans votre classe.

Concernant le message de Keith et sa règle n°4 :

Certaines affiches confondent la règle 3 et la règle 4.La règle 4 de Keith est absolument correcte, sans équivoque.C’est la seule règle des quatre qui ne nécessite aucune modification.Je reformulerais légèrement certaines de ses autres règles pour les rendre plus claires, mais elles sont essentiellement correctes si vous les analysez correctement et si vous lisez l'intégralité du message pour voir comment il les développe.

  1. Si votre classe n'utilise pas de ressource non gérée ET qu'elle n'instancie jamais un autre objet d'une classe qui utilise elle-même, directement ou finalement, un objet non géré (c'est-à-dire une classe qui implémente IDisposable), alors votre classe ne serait pas nécessaire. soit pour implémenter IDisposable lui-même, soit même pour appeler .dispose sur n'importe quoi.(Dans un tel cas, il est idiot de penser que vous DEVEZ de toute façon libérer immédiatement de la mémoire avec un GC forcé.)

  2. Si votre classe utilise une ressource non gérée OU instancie un autre objet qui implémente lui-même IDisposable, alors votre classe doit soit :

    a) les éliminer/libérer immédiatement dans un contexte local dans lequel ils ont été créés, OU...

    b) implémentez IDisposable selon le modèle recommandé dans le message de Keith, ou dans quelques milliers d'endroits sur Internet, ou dans environ 300 livres à l'heure actuelle.

    b.1) De plus, si (b), et qu'il s'agit d'une ressource non gérée qui a été ouverte, IDisposable ET Finalize DEVRAIENT TOUJOURS être implémentés, conformément à la règle n°4 de Keith.
    Dans ce contexte, Finalize EST absolument un filet de sécurité dans un sens :si quelqu'un instancie VOTRE objet IDisposable qui utilise une ressource non gérée et qu'il ne parvient pas à appeler dispose, alors Finalize est la dernière chance pour VOTRE objet de fermer correctement la ressource non gérée.
    (Finalize devrait le faire en appelant Dispose de telle manière que la méthode Dispose ignore la libération de tout sauf la ressource non gérée.Alternativement, si la méthode Dispose de votre objet EST appelée correctement par la personne qui a instancié votre objet, elle transmet à la fois l'appel Dispose à tous les objets IDisposable qu'elle a instanciés ET libère correctement les ressources non gérées, se terminant par un appel pour supprimer le Finalize sur votre objet. , ce qui signifie que l'impact de l'utilisation de Finalize est réduit si votre objet est supprimé correctement par l'appelant.Tous ces points sont inclus dans le message de Keith, BTW.)

    b.2) SI votre classe implémente uniquement IDisposable parce qu'elle doit essentiellement transmettre un Dispose à un objet IDisposable qu'elle a instancié, alors n'implémentez pas de méthode Finalize dans votre classe dans ce cas.Finalize sert à gérer le cas où les DEUX Dispose n'ont jamais été appelés par la personne qui a instancié votre objet ET qu'une ressource non gérée a été utilisée et qui n'est toujours pas publiée.

Bref, concernant le message de Keith, il a tout à fait raison, et ce message est la réponse la plus correcte et la plus complète, à mon avis.Il peut utiliser des déclarations abrégées que certains trouvent « fausses » ou auxquelles s'opposent, mais son message complet développe complètement l'utilisation de Finalize, et il a tout à fait raison.Assurez-vous de lire entièrement son message avant de vous lancer dans l'une des règles ou des déclarations préliminaires de son message.

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