Frage

Der .NET-Garbage Collector wird irgendwann Speicher freigeben, aber was ist, wenn Sie diesen Speicher sofort zurückhaben möchten?Welchen Code müssen Sie in einer Klasse verwenden? MyClass anrufen

MyClass.Dispose()

und geben Sie den gesamten von Variablen und Objekten verwendeten Speicherplatz frei MyClass?

War es hilfreich?

Lösung

IDisposable hat nichts mit der Freigabe von Speicher zu tun.IDisposable ist ein Muster zum Freigeben nicht verwaltet Ressourcen – und Speicher ist definitiv eine verwaltete Ressource.

Die Links, die auf GC.Collect() verweisen, sind die richtige Antwort, obwohl von der Verwendung dieser Funktion in der Microsoft .NET-Dokumentation generell abgeraten wird.

Bearbeiten: Da ich für diese Antwort eine beträchtliche Menge Karma verdient habe, fühle ich mich in gewisser Weise verpflichtet, näher darauf einzugehen, damit ein Neuling in der .NET-Ressourcenverwaltung keinen falschen Eindruck bekommt.

Innerhalb eines .NET-Prozesses gibt es zwei Arten von Ressourcen – verwaltete und nicht verwaltete.„Verwaltet“ bedeutet, dass die Laufzeit die Kontrolle über die Ressource hat, während „nicht verwaltet“ bedeutet, dass sie in der Verantwortung des Programmierers liegt.Und es gibt eigentlich nur eine Art verwalteter Ressource, die uns heute in .NET am Herzen liegt – der Speicher.Der Programmierer weist die Laufzeitumgebung an, Speicher zuzuweisen, und anschließend muss die Laufzeitumgebung herausfinden, wann der Speicher freigegeben werden kann.Der Mechanismus, den .NET zu diesem Zweck verwendet, heißt Müllabfuhr Und Sie können zahlreiche Informationen über GC im Internet finden, indem Sie einfach Google verwenden.

Bei den anderen Arten von Ressourcen weiß .NET nichts über deren Bereinigung und muss sich daher darauf verlassen, dass der Programmierer das Richtige tut.Zu diesem Zweck stellt die Plattform dem Programmierer drei Werkzeuge zur Verfügung:

  1. Die IDisposable-Schnittstelle und die „using“-Anweisung in VB und C#
  2. Finalisierer
  3. Das IDisposable-Muster, wie es von vielen BCL-Klassen implementiert wird

Die erste davon ermöglicht es dem Programmierer, eine Ressource effizient zu erwerben, sie zu nutzen und sie dann alles innerhalb derselben Methode freizugeben.

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

Wenn „AcquireResource“ eine Factory-Methode ist, die (zum Beispiel) eine Datei öffnet und „Dispose“ die Datei automatisch schließt, kann dieser Code keine Dateiressource verlieren.Aber der Speicher für das „tmp“-Objekt selbst kann durchaus noch reserviert sein.Das liegt daran, dass die IDisposable-Schnittstelle überhaupt keine Verbindung zum Garbage Collector hat.Wenn du tat Wenn Sie sicherstellen möchten, dass der Speicher freigegeben wurde, besteht Ihre einzige Möglichkeit darin, anzurufen GC.Collect() um eine Garbage Collection zu erzwingen.

Es kann jedoch nicht genug betont werden, dass dies wahrscheinlich keine gute Idee ist.Im Allgemeinen ist es viel besser, den Garbage Collector das tun zu lassen, wozu er entwickelt wurde, nämlich den Speicher zu verwalten.

Was passiert, wenn die Ressource über einen längeren Zeitraum genutzt wird, sodass sich ihre Lebensdauer über mehrere Methoden erstreckt?Offensichtlich ist die „using“-Anweisung nicht mehr anwendbar, sodass der Programmierer manuell „Dispose“ aufrufen müsste, wenn er oder sie mit der Ressource fertig ist.Und was passiert, wenn der Programmierer es vergisst?Wenn es keinen Fallback gibt, kann es sein, dass dem Prozess oder Computer irgendwann die Ressource ausgeht, die nicht ordnungsgemäß freigegeben wird.

Hier kommen die Finalisierer ins Spiel.Ein Finalizer ist eine Methode Ihrer Klasse, die eine besondere Beziehung zum Garbage Collector hat.Der GC verspricht, dass er – bevor er den Speicher für ein Objekt dieses Typs freigibt – dem Finalizer zunächst die Möglichkeit gibt, eine Art Bereinigung durchzuführen.

Im Falle einer Datei müssen wir die Datei also theoretisch überhaupt nicht manuell schließen.Wir können einfach warten, bis der Garbage Collector es erreicht, und dann den Finalizer die Arbeit machen lassen.Leider funktioniert dies in der Praxis nicht gut, da der Garbage Collector nicht deterministisch läuft.Die Datei bleibt möglicherweise erheblich länger geöffnet, als der Programmierer erwartet.Und wenn genügend Dateien geöffnet bleiben, schlägt das System möglicherweise fehl, wenn es versucht, eine weitere Datei zu öffnen.

Für die meisten Ressourcen wollen wir beides.Wir möchten, dass eine Konvention sagen kann: „Wir sind jetzt mit dieser Ressource fertig“, und wir möchten sicherstellen, dass zumindest eine gewisse Chance besteht, dass die Bereinigung automatisch erfolgt, wenn wir vergessen, sie manuell durchzuführen.Hier kommt das „IDisposable“-Muster ins Spiel.Dies ist eine Konvention, die es IDispose und einem Finalizer ermöglicht, gut zusammenzuspielen.Sie können sehen, wie das Muster funktioniert, indem Sie sich das ansehen offizielle Dokumentation für IDisposable.

Endeffekt: Wenn Sie wirklich nur sicherstellen möchten, dass Speicher freigegeben wird, helfen Ihnen IDisposable und Finalizer nicht weiter.Aber die IDisposable-Schnittstelle ist Teil eines äußerst wichtigen Musters, das alle .NET-Programmierer verstehen sollten.

Andere Tipps

Sie können nur Instanzen entsorgen, die die IDisposable-Schnittstelle implementieren.

So erzwingen Sie, dass eine Garbage Collection den (nicht verwalteten) Speicher sofort freigibt:

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

Dies ist normalerweise eine schlechte Vorgehensweise, aber es gibt beispielsweise einen Fehler in der x64-Version des .NET-Frameworks, der dazu führt, dass sich der GC in einigen Szenarien seltsam verhält, und dann möchten Sie dies möglicherweise tun.Ich weiß nicht, ob der Fehler bereits behoben wurde.Weiß jemand?

Um eine Klasse zu entsorgen, gehen Sie wie folgt vor:

instance.Dispose();

oder so:

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

das wird zur Kompilierungszeit übersetzt in:

MyClass instance = null;    

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

Sie können die IDisposable-Schnittstelle wie folgt implementieren:

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

Die Antworten auf diese Frage sind mehr als nur ein wenig verwirrend.

Der Titel fragt nach der Entsorgung, sagt dann aber, dass sie die Erinnerung sofort zurückhaben wollen.

.Net ist gelang es, was bedeutet, dass Sie sich beim Schreiben von .Net-Apps nicht direkt um den Speicher kümmern müssen, der Preis besteht jedoch darin, dass Sie auch keine direkte Kontrolle über den Speicher haben.

.Net entscheidet, wann es am besten ist, Speicher zu bereinigen und freizugeben, nicht Sie als .Net-Programmierer.

Der Dispose ist eine Möglichkeit, .Net mitzuteilen, dass Sie mit etwas fertig sind, aber der Speicher wird erst dann wirklich freigegeben, wenn der beste Zeitpunkt dafür gekommen ist.

Grundsätzlich wird .Net den Speicher tatsächlich dann zurückholen, wenn es dafür am einfachsten ist – es kann sehr gut entscheiden, wann.Sofern Sie nicht gerade etwas schreiben, das sehr speicherintensiv ist, müssen Sie es normalerweise nicht außer Kraft setzen (dies ist einer der Gründe dafür, dass Spiele noch nicht oft in .Net geschrieben werden – sie benötigen vollständige Kontrolle).

In .Net können Sie verwenden GC.Collect() es sofort zu erzwingen, aber das ist fast immer eine schlechte Praxis.Wenn .Net es noch nicht bereinigt hat, bedeutet das, dass jetzt kein besonders guter Zeitpunkt dafür ist.

GC.Collect() Nimmt die Objekte auf, die .Net als erledigt identifiziert.Wenn Sie ein Objekt, das es benötigt, nicht entsorgt haben, kann .Net entscheiden, dieses Objekt zu behalten.Das bedeutet, dass GC.Collect() ist nur wirksam, wenn Sie Ihre verfügbaren Instanzen korrekt implementieren.

GC.Collect() Ist nicht ein Ersatz für die korrekte Verwendung von IDisposable.

Dispose und Speicher stehen also nicht in direktem Zusammenhang, müssen es aber auch nicht sein.Durch die korrekte Entsorgung werden Ihre .Net-Apps jedoch effizienter und verbrauchen daher weniger Speicher.


In 99 % der Fälle in .Net ist Folgendes die beste Vorgehensweise:

Regel 1: Wenn Sie sich um nichts kümmern nicht verwaltet oder das implementiert IDisposable Dann machen Sie sich keine Sorgen über die Entsorgung.

Regel 2: Wenn Sie eine lokale Variable haben, die IDisposable implementiert, stellen Sie sicher, dass Sie diese im aktuellen Bereich entfernen:

//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();
}

Regel 3: Wenn eine Klasse über eine Eigenschaft oder Mitgliedsvariable verfügt, die IDisposable implementiert, sollte diese Klasse auch IDisposable implementieren.In der Dispose-Methode dieser Klasse können Sie auch Ihre IDisposable-Eigenschaften entsorgen:

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

Dies ist nicht wirklich vollständig, weshalb das Beispiel versiegelt ist.Beim Erben von Klassen muss möglicherweise die nächste Regel beachtet werden ...

Regel 4: Wenn eine Klasse eine verwendet nicht verwaltet Ressource und implementieren Sie dann IDispose Und Fügen Sie einen Finalizer hinzu.

.Net kann damit nichts anfangen nicht verwaltet Ressource, also reden wir jetzt über den Speicher.Wenn Sie es nicht bereinigen, kann es zu einem Speicherverlust kommen.

Die Dispose-Methode muss beides bewältigen gelang es Und nicht verwaltet Ressourcen.

Der Finalizer ist ein Sicherheitsmechanismus – er stellt sicher, dass, wenn jemand anderes eine Instanz Ihrer Klasse erstellt und diese nicht entsorgt, die „gefährliche“ nicht verwaltet Ressourcen können weiterhin von .Net bereinigt werden.

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

Zum Schluss diese Überladung von Dispose, die ein boolesches Flag akzeptiert:

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

Beachten Sie, dass, sobald dies alles erledigt ist, anderer verwalteter Code, der eine Instanz Ihrer Klasse erstellt, diese einfach wie jedes andere IDisposable behandeln kann (Regeln 2 und 3).

Wäre es angebracht, auch zu erwähnen, dass sich dispose nicht immer auf den Speicher bezieht?Ich entsorge Ressourcen wie Verweise auf Dateien häufiger als Speicher.GC.Collect() bezieht sich direkt auf den CLR-Garbage Collector und gibt möglicherweise Speicher frei (im Task-Manager).Es wird sich wahrscheinlich negativ auf Ihre Anwendung auswirken (z. B. auf die Leistung).

Warum wollen Sie am Ende des Tages die Erinnerung sofort zurückhaben?Wenn von einer anderen Stelle Speicherdruck ausgeht, stellt Ihnen das Betriebssystem in den meisten Fällen Speicher zur Verfügung.

Schau dir das an Artikel

Die Implementierung des Dispose-Musters, von IDisposable und/oder eines Finalizers hat absolut nichts damit zu tun, wann Speicher zurückgewonnen wird;Stattdessen geht es vor allem darum, es dem GC zu sagen Wie um diese Erinnerung zurückzugewinnen.Wenn Sie Dispose() aufrufen, interagieren Sie in keiner Weise mit dem GC.

Der GC wird nur dann ausgeführt, wenn er die Notwendigkeit feststellt (Speicherdruck genannt), und dann (und nur dann) wird er Speicher für ungenutzte Objekte freigeben und den Speicherplatz verdichten.

Du könnte Rufen Sie GC.Collect() auf, aber das sollten Sie wirklich nicht tun, es sei denn, es gibt eine sehr guter Grund dafür (was fast immer „Nie“ ist).Wenn Sie einen Out-of-Band-Erfassungszyklus wie diesen erzwingen, veranlassen Sie den GC tatsächlich, mehr Arbeit zu leisten, und können letztendlich die Leistung Ihrer Anwendungen beeinträchtigen.Für die Dauer des GC-Erfassungszyklus befindet sich Ihre Anwendung tatsächlich in einem eingefrorenen Zustand. Je mehr GC-Zyklen ausgeführt werden, desto länger bleibt Ihre Anwendung eingefroren.

Es gibt auch einige native Win32-API-Aufrufe, die Sie durchführen können, um Ihren Arbeitssatz freizugeben, aber selbst diese sollten vermieden werden, es sei denn, es gibt einen sehr guter Grund, es zu tun.

Die ganze Prämisse hinter einer Gargbage-Collected-Laufzeit besteht darin, dass Sie sich nicht (so viele) Gedanken darüber machen müssen, wann die Laufzeit tatsächlichen Speicher zuweist/freigibt;Sie müssen sich nur darum kümmern, sicherzustellen, dass Ihr Objekt weiß, wie es sich selbst aufräumt, wenn Sie dazu aufgefordert werden.

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

dann kannst du so etwas machen

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

oder

using (MyClass instance = new MyClass())
{

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

Vollständige Erklärung von Joe Duffy zu „Entsorgung, Finalisierung und Ressourcenmanagement":

Zu Beginn des Lebens von .NET Framework wurden Finalizer von C# -Programmierern konsequent als Zerstörer bezeichnet.Wenn wir im Laufe der Zeit schlauer werden, versuchen wir, uns mit der Tatsache auseinanderzusetzen, dass die Die Dispose -Methode entspricht einem C ++ - Destructor (deterministisch) wirklich besser, während Finalizer ist etwas ganz getrennt (nicht deterministisch).Die Tatsache, dass C# die C ++ - Destruktorsyntax (dh~ T ()) hatte sicherlich zumindest ein wenig mit der Entwicklung dieser Fehlbezeichnung zu tun.

Ich habe eine Zusammenfassung von Destructors und Dispose and Garbage Collection auf geschrieben http://codingcraftsman.wordpress.com/2012/04/25/to-dispose-or-not-to-dispose/

Um die ursprüngliche Frage zu beantworten:

  1. Versuchen Sie nicht, Ihr Gedächtnis zu verwalten
  2. Bei Dispose geht es nicht um Speicherverwaltung, sondern um die Verwaltung nicht verwalteter Ressourcen
  3. Finalizer sind ein inhärenter Teil des Dispose-Musters und verlangsamen tatsächlich die Speicherfreigabe verwalteter Objekte (da sie in die Finalisierungswarteschlange gestellt werden müssen, sofern sie nicht bereits Dispose d sind).
  4. GC.Collect ist schlecht, da es den Anschein erweckt, dass einige kurzlebige Objekte länger benötigt werden, und so ihre Sammlung verlangsamt.

GC.Collect könnte jedoch nützlich sein, wenn Sie über einen leistungskritischen Codeabschnitt verfügen und die Wahrscheinlichkeit verringern möchten, dass die Garbage Collection ihn verlangsamt.Das nennst du schon mal.

Darüber hinaus gibt es ein Argument, das für dieses Muster spricht:

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

vs

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

Aber die wichtigste Antwort ist, dass die Garbage Collection nur dann funktioniert, wenn Sie damit herumspielen!

Sie können einen GC nicht wirklich zwingen, ein Objekt zu bereinigen, wenn Sie möchten. Es gibt zwar Möglichkeiten, die Ausführung zu erzwingen, aber nichts sagt aus, dass er alle gewünschten/erwarteten Objekte bereinigt.Am besten rufen Sie dispose in einer Try-Catch-Ex-Finally-Dispose-End-Try-Methode (VB.NET rulz) auf.Dispose dient jedoch zum Bereinigen von Systemressourcen (Speicher, Handles, Datenbankverbindungen usw.).vom Objekt auf deterministische Weise zugewiesen.Dispose bereinigt (und kann) nicht den vom Objekt selbst verwendeten Speicher, sondern nur der GC.

Dieser Artikel hat eine ziemlich einfache Komplettlösung.Jedoch, haben Den GC aufzurufen, anstatt ihn seinen natürlichen Lauf nehmen zu lassen, ist im Allgemeinen ein Zeichen für schlechtes Design/Speichermanagement. besonders wenn keine begrenzten Ressourcen verbraucht werden (Verbindungen, Handles, alles andere, was normalerweise zur Implementierung von IDisposable führt).

Was führt dazu, dass Sie dies tun müssen?

Entschuldigung, aber die hier ausgewählte Antwort ist falsch.Wie einige Leute später festgestellt haben, hat Dispose und die Implementierung von IDisposable nichts mit der Freigabe des mit einer .NET-Klasse verbundenen Speichers zu tun.Es wird hauptsächlich und traditionell dazu verwendet, nicht verwaltete Ressourcen wie Dateihandles usw. freizugeben.

Während Ihre Anwendung GC.Collect() aufrufen kann, um zu versuchen, eine Sammlung durch den Garbage Collector zu erzwingen, hat dies nur Auswirkungen auf die Elemente, die sich auf der richtigen Generationsebene in der Freachable-Warteschlange befinden.Wenn Sie also alle Verweise auf das Objekt gelöscht haben, ist es möglich, dass es noch ein paar Aufrufe von GC.Collect() dauert, bis der tatsächliche Speicher freigegeben wird.

Sie sagen in Ihrer Frage nicht, WARUM Sie das Bedürfnis verspüren, sofort Speicher freizugeben.Ich verstehe, dass es manchmal ungewöhnliche Umstände geben kann, aber im Ernst: Bei verwaltetem Code ist es fast immer am besten, die Speicherverwaltung der Laufzeit zu überlassen.

Der wahrscheinlich beste Ratschlag: Wenn Sie der Meinung sind, dass Ihr Code Speicher schneller verbraucht, als der GC ihn freigibt, dann sollten Sie Ihren Code überprüfen, um sicherzustellen, dass in den Datenstrukturen, die Sie in statischen Elementen usw. herumliegen, keine Objekte referenziert werden, die nicht mehr benötigt werden .Vermeiden Sie außerdem Situationen, in denen Sie zirkuläre Objektverweise haben, da diese möglicherweise ebenfalls nicht freigegeben werden.

@Keith,

Ich stimme allen Ihren Regeln zu, außer Nr. 4.Das Hinzufügen eines Finalizers sollte nur unter ganz bestimmten Umständen erfolgen.Wenn eine Klasse nicht verwaltete Ressourcen verwendet, sollten diese in Ihrer Dispose(bool)-Funktion bereinigt werden.Dieselbe Funktion sollte verwaltete Ressourcen nur bereinigen, wenn „bool“ wahr ist.Das Hinzufügen eines Finalizers erhöht die Komplexität der Verwendung Ihres Objekts, da jedes Mal, wenn Sie eine neue Instanz erstellen, diese auch in die Finalisierungswarteschlange gestellt werden muss, die jedes Mal überprüft wird, wenn der GC einen Sammlungszyklus ausführt.Im Endeffekt bedeutet dies, dass Ihr Objekt einen Zyklus/eine Generation länger überlebt, als es sollte, sodass der Finalizer ausgeführt werden kann.Der Finalizer sollte nicht als „Sicherheitsnetz“ betrachtet werden.

Der GC führt nur dann einen Sammlungszyklus durch, wenn er feststellt, dass im Gen0-Heap nicht genügend Speicher für die nächste Zuweisung verfügbar ist, es sei denn, Sie „helfen“ ihm, indem Sie GC.Collect() aufrufen, um eine Out-of-Band-Sammlung zu erzwingen .

Die Quintessenz ist, dass der GC, egal was passiert, nur weiß, wie er Ressourcen freigibt, indem er die Dispose-Methode (und möglicherweise den Finalizer, falls einer implementiert ist) aufruft.Es liegt an dieser Methode, „das Richtige zu tun“, alle verwendeten nicht verwalteten Ressourcen zu bereinigen und alle anderen verwalteten Ressourcen anzuweisen, ihre Dispose-Methode aufzurufen.Es ist in seiner Arbeit sehr effizient und kann sich weitgehend selbst optimieren, solange es nicht durch Out-of-Band-Erfassungszyklen unterstützt wird.Abgesehen davon haben Sie, sofern Sie GC.Collect nicht explizit aufrufen, keine Kontrolle darüber, wann und in welcher Reihenfolge Objekte entsorgt und Speicher freigegeben werden.

Wenn MyClass IDisposable implementiert, können Sie genau das tun.

MyClass.Dispose();

Best Practice in C# ist:

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

Dadurch wird die Entsorgung in einem Try-finally abgeschlossen und sichergestellt, dass sie nie übersehen wird.

Wenn Sie IDisposable nicht in Ihrer Klasse implementieren möchten (oder können), können Sie die Speicherbereinigung wie folgt erzwingen (aber es ist langsam) -

GC.Collect();

Die IDisposable-Schnittstelle ist eigentlich für Klassen gedacht, die nicht verwaltete Ressourcen enthalten.Wenn Ihre Klasse keine nicht verwalteten Ressourcen enthält, warum tun Sie das? brauchen um Ressourcen freizugeben, bevor der Garbage Collector dies tut?Andernfalls stellen Sie einfach sicher, dass Ihr Objekt so spät wie möglich instanziiert wird und so schnell wie möglich den Gültigkeitsbereich verlässt.

In C++ ist eine deterministische Objektzerstörung möglich

Sie möchten GC.Collect niemals aufrufen, da dies die Selbstoptimierung des Garbage Collectors beeinträchtigt, um Speicherauslastung zu erkennen, und in einigen Fällen nichts anderes tut, als die aktuelle Generation jedes Objekts auf dem Heap zu erhöhen.

Für diejenigen, die IDisposable-Antworten posten.Durch den Aufruf einer Dispose-Methode wird ein Objekt nicht zerstört, wie der Fragesteller es beschreibt.

@Keith:

IDisposable ist für verwaltete Ressourcen.

Finalizer sind für nicht verwaltete Ressourcen vorgesehen.

Tut mir leid, aber das ist einfach falsch.Normalerweise macht der Finalizer überhaupt nichts.Wenn jedoch die Muster entsorgen korrekt implementiert wurde, versucht der Finalizer aufzurufen Dispose.

Dispose hat zwei Aufgaben:

  • Kostenlose nicht verwaltete Ressourcen und
  • freie verschachtelte verwaltete Ressourcen.

Und hier kommt Ihre Aussage ins Spiel, denn es stimmt, dass ein Objekt beim Finalisieren niemals versuchen sollte, verschachtelte verwaltete Ressourcen freizugeben, da diese möglicherweise bereits freigegeben wurden.Es müssen jedoch weiterhin nicht verwaltete Ressourcen freigegeben werden.

Dennoch haben Finalisierer keine andere Aufgabe, als anzurufen Dispose und weisen Sie es an, verwaltete Objekte nicht zu berühren. Dispose, bei manuellem Aufruf (oder per Using), soll alle nicht verwalteten Ressourcen freigeben und übergeben Dispose Nachricht an verschachtelte Objekte (und Basisklassenmethoden) weiterleiten, dies wird jedoch der Fall sein niemals Geben Sie jeglichen (verwalteten) Speicher frei.

Konrad Rudolph – ja, normalerweise macht der Finalizer überhaupt nichts.Sie sollten es nicht implementieren, es sei denn, Sie haben es mit nicht verwalteten Ressourcen zu tun.

Wenn Sie es dann implementieren, verwenden Sie Microsofts Entsorgungsmuster (wie bereits beschrieben)

  • public Dispose() Anrufe protected Dispose(true) - befasst sich sowohl mit verwalteten als auch mit nicht verwalteten Ressourcen.Berufung Dispose() sollte die Finalisierung unterdrücken.

  • ~Finalize Anrufe protected Dispose(false) – befasst sich nur mit nicht verwalteten Ressourcen.Dies verhindert nicht verwaltete Speicherlecks, wenn Sie den nicht aufrufen public Dispose()

~Finalize ist langsam und sollte nicht verwendet werden, es sei denn, Sie müssen mit nicht verwalteten Ressourcen umgehen.

Verwaltete Ressourcen können keinen Speicherverlust verursachen, sie können nur Ressourcen für die aktuelle Anwendung verschwenden und deren Speicherbereinigung verlangsamen.Nicht verwaltete Ressourcen können auslaufen und ~Finalize ist die beste Vorgehensweise, um sicherzustellen, dass dies nicht der Fall ist.

In beiden Fällen using ist Best Practice.

@Curt Hagenlocher – das ist von hinten nach vorne.Ich habe keine Ahnung, warum so viele dafür gestimmt haben, obwohl es falsch ist.

IDisposable ist für gelang es Ressourcen.

Finaliser sind für nicht verwaltet Ressourcen.

Solange Sie nur verwaltete Ressourcen verwenden, haben sowohl @Jon Limjap als auch ich völlig Recht.

Für Klassen, die nicht verwaltete Ressourcen verwenden (und bedenken Sie, dass dies bei der überwiegenden Mehrheit der .Net-Klassen nicht der Fall ist), ist Patriks Antwort umfassend und bewährt.

Vermeiden Sie die Verwendung von GC.Collect – es ist eine langsame Methode, mit verwalteten Ressourcen umzugehen, und hat nichts mit nicht verwalteten Ressourcen zu tun, es sei denn, Sie haben Ihre ~Finalizer korrekt erstellt.


Ich habe den Moderatorkommentar entsprechend aus der ursprünglichen Frage entfernt https://stackoverflow.com/questions/14593/etiquette-for-modifying-posts

Als Antwort auf die ursprüngliche Frage ist es aufgrund der bisherigen Informationen des Originalposters zu 100 % sicher, dass er nicht genug über die Programmierung in .NET weiß, um überhaupt die Antwort zu erhalten:Verwenden Sie GC.Collect().Ich würde sagen, es ist zu 99,99 % wahrscheinlich, dass er GC.Collect() überhaupt nicht verwenden muss, wie die meisten Poster betont haben.

Die richtige Antwort lautet: „Lassen Sie den GC seine Arbeit machen.“Zeitraum.Sie müssen sich um andere Dinge kümmern.Aber vielleicht möchten Sie überlegen, ob und wann Sie bestimmte Objekte entsorgen oder bereinigen sollten und ob Sie IDisposable und möglicherweise Finalize in Ihrer Klasse implementieren müssen.'

Bezüglich Keiths Beitrag und seiner Regel Nr. 4:

Einige Poster verwechseln Regel 3 und Regel 4.Keiths Regel 4 ist absolut richtig und eindeutig.Es ist die einzige der vier Regeln, die überhaupt keiner Bearbeitung bedarf.Ich würde einige seiner anderen Regeln leicht umformulieren, um sie klarer zu machen, aber sie sind im Wesentlichen richtig, wenn Sie sie richtig analysieren und tatsächlich den gesamten Beitrag lesen, um zu sehen, wie er sie erweitert.

  1. Wenn Ihre Klasse keine nicht verwaltete Ressource verwendet UND außerdem niemals ein anderes Objekt einer Klasse instanziiert, die selbst direkt oder letztendlich ein nicht verwaltetes Objekt verwendet (d. h. eine Klasse, die IDisposable implementiert), besteht keine Notwendigkeit für Ihre Klasse um entweder IDisposable selbst zu implementieren oder sogar .dispose für irgendetwas aufzurufen.(In einem solchen Fall ist es sowieso albern zu glauben, dass Sie mit einem erzwungenen GC tatsächlich sofort Speicher freigeben MÜSSEN.)

  2. Wenn Ihre Klasse eine nicht verwaltete Ressource verwendet ODER ein anderes Objekt instanziiert, das selbst IDisposable implementiert, sollte Ihre Klasse entweder:

    a) diese sofort in einem lokalen Kontext entsorgen/freigeben, in dem sie erstellt wurden, ODER...

    b) Implementieren Sie IDisposable nach dem in Keiths Beitrag empfohlenen Muster oder an einigen tausend Stellen im Internet oder mittlerweile buchstäblich in etwa 300 Büchern.

    b.1) Wenn (b) außerdem eine nicht verwaltete Ressource geöffnet wurde, SOLLTEN gemäß Keiths Regel Nr. 4 IMMER sowohl IDisposable als auch Finalize implementiert werden.
    In diesem Zusammenhang IST Finalize in gewisser Hinsicht absolut ein Sicherheitsnetz:Wenn jemand IHR IDisposable-Objekt instanziiert, das eine nicht verwaltete Ressource verwendet, und dispose nicht aufrufen kann, ist Finalize die letzte Chance für IHR Objekt, die nicht verwaltete Ressource ordnungsgemäß zu schließen.
    (Finalize sollte dies tun, indem es Dispose so aufruft, dass die Dispose-Methode die Freigabe von etwas ABER der nicht verwalteten Ressource überspringt.Wenn alternativ die Dispose-Methode Ihres Objekts ordnungsgemäß von dem aufgerufen wird, was auch immer Ihr Objekt instanziiert hat, dann leitet sie BEIDE den Dispose-Aufruf an alle IDisposable-Objekte weiter, die sie instanziiert hat, UND gibt die nicht verwalteten Ressourcen ordnungsgemäß frei, was mit einem Aufruf endet, um Finalize für Ihr Objekt zu unterdrücken , was bedeutet, dass die Auswirkungen der Verwendung von Finalize verringert werden, wenn Ihr Objekt vom Aufrufer ordnungsgemäß entsorgt wird.Alle diese Punkte sind übrigens in Keiths Beitrag enthalten.)

    b.2) WENN Ihre Klasse IDisposable nur implementiert, weil sie im Wesentlichen ein Dispose an ein von ihr instanziiertes IDisposable-Objekt weitergeben muss, implementieren Sie in diesem Fall keine Finalize-Methode in Ihrer Klasse.Finalize dient dazu, den Fall zu behandeln, dass BOTH Dispose nie von dem Instanziierten Ihres Objekts aufgerufen wurde UND eine nicht verwaltete Ressource verwendet wurde, die noch nicht freigegeben ist.

Kurz gesagt, was Keiths Beitrag angeht, hat er völlig Recht, und dieser Beitrag ist meiner Meinung nach die richtigste und vollständigste Antwort.Er verwendet möglicherweise einige kurze Aussagen, die manche als „falsch“ empfinden oder denen sie widersprechen, aber sein vollständiger Beitrag geht ausführlich auf die Verwendung von Finalize ein und er hat absolut Recht.Lesen Sie seinen Beitrag unbedingt vollständig durch, bevor Sie sich auf eine der Regeln oder vorläufigen Aussagen in seinem Beitrag stürzen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top