1897 Stimmen

Richtige Verwendung der IDisposable-Schnittstelle

Ich weiß vom Lesen die Microsoft-Dokumentation dass die "primäre" Verwendung des IDisposable Schnittstelle ist es, nicht verwaltete Ressourcen zu bereinigen.

Für mich bedeutet "nicht verwaltet" Dinge wie Datenbankverbindungen, Sockets, Fensterhandles usw. Ich habe aber auch schon Code gesehen, bei dem die Dispose() Methode ist implementiert, um die verwaltet Ressourcen, was mir überflüssig erscheint, da der Garbage Collector dies für Sie erledigen sollte.

Zum Beispiel:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }

Meine Frage ist, macht dies den Garbage Collector frei Speicher verwendet von MyCollection schneller als normalerweise?

edit : Bislang wurden einige gute Beispiele für die Verwendung von IDisposable zum Aufräumen von nicht verwalteten Ressourcen wie Datenbankverbindungen und Bitmaps vorgestellt. Aber nehmen wir an, dass _theList im obigen Code eine Million Zeichenfolgen enthielt und Sie diesen Speicher freigeben wollten jetzt anstatt auf den Garbage Collector zu warten. Würde der obige Code das erreichen?

13voto

mqp Punkte 66863

Ja, dieser Code ist völlig überflüssig und unnötig, und er bringt den Garbage Collector nicht dazu, etwas zu tun, was er sonst nicht tun würde (sobald eine Instanz von MyCollection den Gültigkeitsbereich verlässt, d. h.). .Clear() Anrufe.

Antwort auf Ihre Frage: Irgendwie schon. Wenn ich das mache:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC

In Bezug auf die Speicherverwaltung ist sie funktional identisch mit dieser:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

Wenn Sie den Speicher wirklich, wirklich, wirklich sofort freigeben müssen, rufen Sie GC.Collect() . Es gibt jedoch keinen Grund, dies hier zu tun. Der Speicher wird wieder freigegeben, wenn er gebraucht wird.

12voto

Drew Noakes Punkte 282438

Si MyCollection sowieso in den Müll wandert, sollten Sie es nicht entsorgen müssen. Dadurch wird die CPU mehr als nötig beansprucht, und es kann sogar vorkalkulierte Analysen, die der Garbage Collector bereits durchgeführt hat, ungültig machen.

Ich benutze IDisposable um z. B. sicherzustellen, dass Threads korrekt entsorgt werden, zusammen mit nicht verwalteten Ressourcen.

EDIT Als Antwort auf Scotts Kommentar:

Der einzige Zeitpunkt, zu dem die GC-Leistungsmetriken betroffen sind, ist der Aufruf von GC.Collect()".

Der GC verwaltet einen Überblick über den Objektreferenzgraphen und alle Verweise auf diesen Graphen in den Stackframes der Threads. Dieser Heap kann recht groß sein und viele Seiten des Speichers umfassen. Zur Optimierung speichert der GC seine Analyse von Seiten, die sich wahrscheinlich nicht oft ändern, im Cache, um ein unnötiges erneutes Scannen der Seite zu vermeiden. Der GC wird vom Kernel benachrichtigt, wenn sich Daten auf einer Seite ändern, so dass er weiß, dass die Seite verschmutzt ist und neu gescannt werden muss. Wenn sich die Sammlung in Gen0 befindet, ist es wahrscheinlich, dass sich auch andere Dinge auf der Seite ändern, aber in Gen1 und Gen2 ist dies weniger wahrscheinlich. Dem Team, das den GC auf den Mac portiert hat, um das Silverlight-Plug-in auf dieser Plattform zum Laufen zu bringen, standen diese Hooks unter Mac OS X anekdotischerweise nicht zur Verfügung.

Ein weiterer Punkt gegen die unnötige Entsorgung von Ressourcen: Stellen Sie sich eine Situation vor, in der ein Prozess entladen wird. Stellen Sie sich auch vor, dass der Prozess schon einige Zeit läuft. Wahrscheinlich sind viele der Speicherseiten dieses Prozesses auf die Festplatte ausgelagert worden. Zumindest befinden sie sich nicht mehr im L1- oder L2-Cache. In einer solchen Situation macht es für eine Anwendung, die entladen wird, keinen Sinn, all diese Daten- und Codeseiten wieder in den Speicher auszulagern, um Ressourcen "freizugeben", die beim Beenden des Prozesses ohnehin vom Betriebssystem freigegeben werden. Dies gilt für verwaltete und sogar für bestimmte nicht verwaltete Ressourcen. Nur Ressourcen, die Nicht-Hintergrund-Threads am Leben erhalten, müssen entsorgt werden, sonst bleibt der Prozess am Leben.

Nun gibt es während der normalen Ausführung ephemere Ressourcen, die korrekt aufgeräumt werden müssen (wie @fezmonkey anmerkt Datenbankverbindungen, Sockets, Fenstergriffe ), um unkontrollierte Speicherlecks zu vermeiden. Dies sind die Arten von Dingen, die entsorgt werden müssen. Wenn Sie eine Klasse erstellen, die einen Thread besitzt (und mit besitzt meine ich, dass sie ihn erstellt hat und daher dafür verantwortlich ist, dass er anhält, zumindest nach meinem Kodierungsstil), dann muss diese Klasse höchstwahrscheinlich Folgendes implementieren IDisposable und reißt den Faden während Dispose .

Das .NET-Framework verwendet die IDisposable Schnittstelle als Signal, ja sogar als Warnung, an die Entwickler, dass die Klasse muss entsorgt werden. Ich kann mich an keine Typen im Framework erinnern, die IDisposable (ausgenommen explizite Schnittstellenimplementierungen), bei denen die Entsorgung optional ist.

8voto

pipTheGeek Punkte 2663

Ich werde nicht die üblichen Dinge über die Verwendung oder Freigabe nicht verwalteter Ressourcen wiederholen, das wurde alles schon behandelt. Aber ich möchte auf ein verbreitetes Missverständnis hinweisen.
Bei folgendem Code

Public Class LargeStuff
  Implements IDisposable
  Private \_Large as string()

  'Some strange code that means \_Large now contains several million long strings.

  Public Sub Dispose() Implements IDisposable.Dispose
    \_Large=Nothing
  End Sub

Mir ist klar, dass die Einweg-Implementierung nicht den aktuellen Leitlinien entspricht, aber ich hoffe, dass Sie alle die Idee verstehen.
Wenn nun Dispose aufgerufen wird, wie viel Speicher wird dann freigegeben?

Antwort: Keine.
Der Aufruf von Dispose kann nicht verwaltete Ressourcen freigeben, er kann verwalteten Speicher NICHT zurückfordern, nur der GC kann das tun. Das heißt nicht, dass das oben genannte nicht eine gute Idee ist, nach dem obigen Muster ist immer noch eine gute Idee in der Tat. Sobald Dispose ausgeführt wurde, gibt es nichts, was die GC daran hindert, den Speicher, der von _Large verwendet wurde, zurückzufordern, auch wenn die Instanz von LargeStuff noch im Gültigkeitsbereich sein kann. Die Strings in _Large können auch in gen 0 sein, aber die Instanz von LargeStuff könnte gen 2 sein, so dass auch hier der Speicher früher wieder freigegeben wird.
Es macht keinen Sinn, einen Finalisierer hinzuzufügen, um die oben gezeigte Dispose-Methode aufzurufen. Das wird nur verzögern die Rückforderung von Speicher, damit der Finalisierer zu laufen.

7voto

Robert Paulson Punkte 16995

In dem von Ihnen geposteten Beispiel wird der Speicher immer noch nicht "jetzt freigegeben". Der gesamte Speicher wird gelöscht, aber es kann sein, dass der Speicher zu einem früheren Zeitpunkt gelöscht wird. Generation . Um sicher zu sein, müssten Sie einige Tests durchführen.


Die Rahmenrichtlinien für die Gestaltung sind Leitlinien und keine Vorschriften. Sie sagen Ihnen, wofür die Schnittstelle in erster Linie gedacht ist, wann sie zu verwenden ist, wie sie zu verwenden ist und wann sie nicht verwendet werden sollte.

Ich habe einmal Code gelesen, der ein einfaches RollBack() bei Fehlschlag unter Verwendung von IDisposable war. Die MiniTx-Klasse unten würde ein Flag auf Dispose() überprüfen und wenn die Commit Anruf nie erfolgte, würde es dann Rollback auf sich selbst. Es fügte eine indirekte Ebene hinzu, die den aufrufenden Code viel einfacher zu verstehen und zu pflegen machte. Das Ergebnis sah in etwa so aus:

using( MiniTx tx = new MiniTx() )
{
    // code that might not work.

    tx.Commit();
} 

Ich habe auch schon gesehen, dass der Timing-/Protokollierungscode das Gleiche tut. In diesem Fall hielt die Methode Dispose() den Timer an und protokollierte, dass der Block beendet wurde.

using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
    // code to time...
}

Hier sind ein paar konkrete Beispiele, die keine unverwaltete Ressourcensäuberung durchführen, sondern IDisposable erfolgreich nutzen, um saubereren Code zu erstellen.

7voto

franckspike Punkte 1734

Wenn Sie möchten, dass sofort löschen verwenden unverwalteter Speicher .

Siehe:

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X