43 votes

Comment se débarrasser d'une classe en .net?

Le ramasse-miettes .net finira par libérer de la mémoire, mais si vous voulez récupérer cette mémoire immédiatement? Quel code devez-vous utiliser dans une classe myclass pour appeler

 myclass.dispose
 

et libérer tout l'espace utilisé par les variables et les objets dans myclass ?

101voto

Curt Hagenlocher Points 12432

IDisposable n'a rien à voir avec la libération de la mémoire. IDisposable est un modèle pour les libérer non géré ressources, et la mémoire est très certainement une ressource gérée.

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

Edit: après Avoir gagné une importante quantité de karma pour cette réponse, je ressens une certaine responsabilité d'élaborer sur elle, de peur qu'un nouveau venu .NET de la gestion des ressources obtenir une fausse impression.

À l'intérieur d'un .NET processus, il y a deux types de ressources gérées et non gérées. "Gérés" signifie que le moteur d'exécution est dans le contrôle de la ressource, alors que les "non géré" signifie que c'est le programmeur de la responsabilité. Et il n'y a vraiment qu'un seul type de ressource gérée que nous nous soucions de dans .NET aujourd'hui -- la mémoire. Le programmeur raconte l'exécution d'allouer de la mémoire et après que c'est au moment de l'exécution à la figure lorsque la mémoire libérée. Le mécanisme .NET utilise à cet effet est appelé la collecte des ordures et vous pouvez trouver beaucoup d'informations sur GC sur l'internet tout simplement en utilisant Google.

Pour les autres types de ressources .NET ne sait rien au sujet de nettoyage de façon à ce qu'elle doit compter sur le programmeur de faire la bonne chose. À cette fin, la plate-forme donne au programmeur de trois outils:

  1. L'interface IDisposable et de l'instruction "using" en VB et C#
  2. Les finaliseurs
  3. La IDisposable modèle mis en place par de nombreux BCL classes

La première de ces permet au programmeur de manière efficace l'acquisition d'une ressource, de l'utiliser et de le relâcher tous au sein de 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 de fabrique que (par exemple) ouvre un fichier et de le "Jeter" se ferme automatiquement le fichier, ce code ne peut pas la fuite d'un fichier de ressources. Mais la mémoire de la "tmp" de l'objet lui-même peut bien encore être attribués. C'est parce que l'interface IDisposable a absolument aucun lien avec le garbage collector. Si vous ne voulez vous assurer que la mémoire a été libérée, votre seule option serait d'appeler GC.Collect() de la force d'un garbage collection.

Toutefois, il ne peut pas être assez souligné que ce n'est probablement pas une bonne idée. Il est généralement beaucoup mieux de laisser le garbage collector de faire ce qu'il a été conçu pour faire, qui est de gérer la mémoire.

Qu'advient-il si la ressource est utilisée pendant une longue période de temps, de telle sorte que sa durée de vie traverse plusieurs méthodes? Clairement, le "à l'aide de la" déclaration n'est pas applicable, de sorte que le programmeur devrait manuellement à l'appel de "Jeter" quand il ou elle se fait avec la ressource. Et qu'advient-il si le programmeur oublie? Si il n'y a pas de secours, puis le processus ou l'ordinateur peut éventuellement exécuter de n'importe quel ressource n'est pas bien libéré.

C'est là que les finaliseurs venir. Un finaliseur est une méthode de la classe qui a une relation particulière avec le garbage collector. Le GC promet que, avant de libérer la mémoire pour un objet de ce type -- il va d'abord donner le finaliseur une chance de faire du nettoyage.

Ainsi, dans le cas d'un fichier, nous avons théoriquement n'avez pas besoin de fermer le fichier manuellement. On peut juste attendre jusqu'à ce que le garbage collector obtient à elle, puis laissez-le finaliseur faire le travail. Malheureusement, cela ne fonctionne pas bien, dans la pratique, parce que le garbage collector s'exécute non-déterministe. Le fichier peut rester ouverte considérablement plus longue que le programmeur attend. Et si assez de fichiers sont ouverts, le système peut échouer lorsque vous essayez d'ouvrir un fichier supplémentaire.

Pour la plupart des ressources, nous voulons à la fois de ces choses. Nous voulons une convention de pouvoir dire "nous en avons terminé avec cette ressource maintenant" et nous voulons nous assurer qu'il y a au moins un peu de chance pour que le nettoyage se produise automatiquement si on oublie de le faire manuellement. C'est là que le "IDisposable" modèle entre en jeu. C'est une convention qui permet IDispose et un finaliseur à jouer très bien ensemble. Vous pouvez voir comment le modèle fonctionne en regardant la documentation officielle pour IDisposable.

Bottom line: Si ce que vous voulez vraiment, c'est juste assurez-vous que la mémoire est libérée, puis IDisposable et les finaliseurs ne sera pas vous aider. Mais l'interface IDisposable fait partie d'un modèle extrêmement important que tous .NET les programmeurs doivent comprendre.

23voto

Patrik Points 5315

Vous ne pouvez disposer instances qui implémentent l'interface IDisposable.

Pour forcer une poubelle recueillir, pour libérer de la (non géré) mémoire:

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

Normalement, c'est une mauvaise pratique, mais il y a par exemple un bug dans la version x64 version de la .NET framework qui fait la GC se comportent bizarre dans certains scénarios, et alors que vous pourriez vouloir faire cela. Je ne sais pas si le bug ont été encore résolue. Personne ne sait?

De disposer d'une classe pour ce faire:

instance.Dispose();

ou comme ceci:

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

qui va se traduire au moment de la compilation à:

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;
    }
}

19voto

Keith Points 46288

Les réponses à cette question ont obtenu plus que un peu confus.

Le titre de la demande au sujet de l'élimination, mais alors dit qu'ils veulent de la mémoire de revenir immédiatement.

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

.Net qui décide du moment où il est préférable de nettoyer et libérer de la mémoire, de ne pas vous le .Net codeur.

L' Dispose est une façon de dire .Net que vous avez terminé avec quelque chose, mais il ne sera pas effectivement libérer de la mémoire jusqu'à ce qu'il est le meilleur moment pour le faire.

Fondamentalement .Net la collecte de la mémoire quand il est plus facile pour lui de le faire - c'est très bon à décider quand. Sauf si vous écrivez quelque chose de très gourmands en mémoire que vous n'avez normalement pas besoin de passer outre (c'est en partie pourquoi les jeux ne sont pas souvent dans les .Net encore: ils ont besoin d'un contrôle complet)

Dans .Net, vous pouvez utiliser GC.Collect() pour le forcer à immédiatement, mais qui est presque toujours une mauvaise pratique. Si .Net n'a pas nettoyé, mais cela signifie qu'il n'est pas particulièrement bon temps pour elle de le faire.

GC.Collect() ramasse les objets .Net identifie comme avec. Si vous n'avez pas disposé d'un objet qui en a besoin .Net peut décider de garder l'objet. Cela signifie qu' GC.Collect() n'est efficace que si vous avez correctement mettre en œuvre votre jetables instances.

GC.Collect() est pas un remplacement pour correctement à l'aide de IDisposable.

Donc en Disposer et la mémoire ne sont pas directement liés, mais ils n'ont pas besoin de l'être. Correctement l'élimination fera de votre .Net applications plus efficace et donc d'utiliser moins de mémoire mais.


99% du temps .Net voici les meilleures pratiques:

Règle 1: Si vous ne travaillez pas avec n'importe quoi non gérés ou qui implémente IDisposable alors ne vous inquiétez pas à propos de les Éliminer.

Règle 2: Si vous avez une variable locale qui implémente IDisposable assurez-vous que vous en débarrasser dans le champ d'application actuel:

//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 d'une variable de membre qui met en œuvre IDisposable alors que la classe doit implémenter IDisposable trop. Dans cette classe, la méthode dispose vous pouvez également vous débarrasser de vos IDisposable propriétés:

//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é. Héritant des classes peuvent avoir besoin pour observer la règle suivante...

Règle 4: Si une classe utilise une non géré ressources puis de mettre en œuvre IDispose et ajouter un finaliser.

.Net ne peut pas faire n'importe quoi avec la non managé des ressources, de sorte que maintenant nous parlons de la mémoire. Si vous n'avez pas le nettoyer, vous pouvez obtenir une fuite de mémoire.

La méthode dispose a besoin pour traiter aussi bien gérés et non gérés ressources.

Le finaliser est un cran de sécurité - il s'assure que si quelqu'un d'autre crée et de l'instance de votre classe et ne parvient pas à éliminer le "dangereux" non géré les ressources peuvent encore être nettoyé 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 Disposer que prend un drapeau 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 ceci est en place autre code géré la création d'une instance de votre classe peut juste le traiter comme n'importe quel autre IDisposable (Règles 2 et 3).

14voto

Brian Lyttle Points 9344

Serait-il opportun de mentionner également que dispose n'est pas toujours vous référer à la mémoire? - Je disposer des ressources telles une des références de fichiers de plus en plus souvent que de mémoire. GC.Collect() est directement liée à la CLR garbage collector et peut ou peut ne pas libérer la mémoire (dans le Gestionnaire des Tâches). Il aura probablement une incidence sur votre demande de façon négative (par exemple, performance).

À la fin de la journée, pourquoi voulez-vous la mémoire tout de suite? Si il y a la pression de la mémoire d'ailleurs le système d'exploitation, vous obtiendrez la mémoire dans la plupart des cas.

6voto

Scott Dorman Points 25000

Jetez un oeil à cet article

Mettre en œuvre le modèle dispose, IDisposable, et/ou un finaliseur a absolument rien à voir avec de la mémoire obtient récupérée; au lieu de cela, il a tout à voir avec dire la GC comment faire pour récupérer de la mémoire. Lorsque vous appelez Dispose() vous êtes en aucune façon d'interagir avec le GC.

Le GC ne courir quand il détermine la nécessité de (appelée pression de mémoire) et ensuite (et seulement ensuite), il sera de libérer de la mémoire pour les objets inutilisés et compact, l'espace de la mémoire.

Vous pourriez appeler GC.Collect (), mais vous ne devriez vraiment pas moins qu'il y est une très bonne raison (qui est presque toujours "Jamais"). Lorsque vous forcer un out-of-band cycle de collecte de données comme cela, vous pouvez effectivement causer la GC pour faire plus de travail et, finalement, peut blesser vos demandes de performances. Pour la durée de la GC cycle de collecte de votre application est en fait dans un état congelé...plus GC cycles qu'à courir, le plus de temps de votre demande passe congelés.

Il y a aussi quelques API Win32 natif des appels, vous pouvez faire votre travail ensemble, mais même ceux-ci devraient être évitée sauf si il y a une très bonne raison de le faire.

La prémisse derrière un gargbage recueillies runtime est que vous n'avez pas besoin de s'inquiéter (comme beaucoup) sur lorsque le moteur d'exécution alloue/libère la mémoire réelle, vous ne devez pas vous inquiéter au sujet de s'assurer que votre objet ne sait comment nettoyer après lui-même quand on lui demande.

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