Update (1. Dezember 2009):
Ich möchte diese Antwort ändern und zugeben, dass die ursprüngliche Antwort fehlerhaft war.
Die ursprüngliche Analyse gilt für Objekte, die finalisiert werden müssen - und der Punkt, an dem Praktiken ohne ein genaues, tiefgreifendes Verständnis nicht an der Oberfläche akzeptiert werden sollten, bleibt bestehen.
Es stellt sich jedoch heraus, dass DataSets, DataViews und DataTables die Finalisierung in ihren Konstruktoren unterdrücken. Aus diesem Grund führt der explizite Aufruf von Dispose () für sie nichts aus.
Vermutlich geschieht dies, weil sie nicht über nicht verwaltete Ressourcen verfügen. Trotz der Tatsache, dass MarshalByValueComponent nicht verwaltete Ressourcen berücksichtigt, sind diese speziellen Implementierungen nicht erforderlich und können daher auf die Finalisierung verzichten.
(Dass .NET-Autoren darauf achten würden, die Finalisierung für genau die Typen zu unterdrücken, die normalerweise den meisten Speicher belegen, spricht für die Bedeutung dieser Vorgehensweise im Allgemeinen für finalisierbare Typen.)
Ungeachtet dessen ist es ziemlich überraschend, dass diese Details seit der Einführung von .NET Framework (vor fast 8 Jahren) immer noch nicht ausreichend dokumentiert sind (dass Sie im Wesentlichen Ihren eigenen Geräten überlassen bleiben, um widersprüchliches, mehrdeutiges Material zu sichten, um die Teile zusammenzusetzen ist manchmal frustrierend, bietet aber ein umfassenderes Verständnis des Rahmens, auf den wir uns täglich verlassen).
Nach vielem Lesen verstehe ich Folgendes:
Wenn ein Objekt Abschluss erfordert, es könnte Speicher länger einnehmen als nötig - hier ist der Grund: a) Jede Art , die eine destructor (oder erbt von einem Typ definiert , dass ein destructor definiert) finalizable betrachtet wird; b) Bei der Zuweisung (bevor der Konstruktor ausgeführt wird) wird ein Zeiger auf die Finalisierungswarteschlange gesetzt. c) Für ein finalisierbares Objekt müssen normalerweise 2 Sammlungen zurückgefordert werden (anstelle von Standard 1). d) Durch das Unterdrücken der Finalisierung wird kein Objekt aus der Finalisierungswarteschlange entfernt (wie von! FinalizeQueue in SOS gemeldet). Dieser Befehl ist irreführend. Es ist nicht hilfreich zu wissen, welche Objekte sich in der Finalisierungswarteschlange befinden (an und für sich). Es wäre hilfreich zu wissen, welche Objekte sich in der Finalisierungswarteschlange befinden und noch finalisiert werden müssen (gibt es dafür einen Befehl?)
Das Unterdrücken der Finalisierung wird im Header des Objekts etwas deaktiviert, um der Laufzeit anzuzeigen, dass der Finalizer nicht aufgerufen werden muss (die FReachable-Warteschlange muss nicht verschoben werden). Es bleibt in der Finalisierungswarteschlange (und wird weiterhin von! FinalizeQueue in SOS gemeldet).
Die Klassen DataTable, DataSet und DataView basieren alle auf MarshalByValueComponent, einem finalisierbaren Objekt, das (möglicherweise) nicht verwaltete Ressourcen verarbeiten kann
- Da DataTable, DataSet und DataView keine nicht verwalteten Ressourcen einführen, unterdrücken sie die Finalisierung in ihren Konstruktoren
- Dies ist zwar ein ungewöhnliches Muster, befreit den Anrufer jedoch von der Notwendigkeit, Dispose nach der Verwendung anzurufen
- Dies und die Tatsache, dass DataTables möglicherweise für verschiedene DataSets freigegeben werden können, ist wahrscheinlich der Grund, warum DataSets untergeordnete DataTables nicht entsorgen möchten
- Dies bedeutet auch, dass diese Objekte in SOS unter! FinalizeQueue angezeigt werden
- Diese Objekte sollten jedoch nach einer einzigen Sammlung wie ihre nicht finalisierbaren Gegenstücke weiterhin zurückgefordert werden können
4 (neue Referenzen):
Ursprüngliche Antwort:
Es gibt viele irreführende und im Allgemeinen sehr schlechte Antworten darauf - jeder, der hier gelandet ist, sollte den Lärm ignorieren und die folgenden Referenzen sorgfältig lesen.
Ohne Zweifel entsorgen sollte werden auf irgendwelchen Finalizable Objekte bezeichnet.
DataTables sind finalisierbar.
Calling Dispose beschleunigt die Speicherwiederherstellung erheblich .
MarshalByValueComponent ruft GC.SuppressFinalize (this) in Dispose () auf. Wenn Sie dies überspringen, müssen Sie auf Dutzende, wenn nicht Hunderte von Gen0-Sammlungen warten, bevor der Speicher wiederhergestellt wird:
Mit diesem grundlegenden Verständnis der Finalisierung können wir bereits einige sehr wichtige Dinge ableiten:
Erstens leben Objekte, die finalisiert werden müssen, länger als Objekte, die nicht finalisiert werden müssen. Tatsächlich können sie viel länger leben. Angenommen, ein Objekt in Gen2 muss finalisiert werden. Die Finalisierung wird geplant, aber das Objekt befindet sich noch in Gen2, sodass es erst bei der nächsten Gen2-Sammlung erneut erfasst wird. Das könnte in der Tat eine sehr lange Zeit sein, und wenn die Dinge gut laufen, wird es in der Tat eine lange Zeit sein, da Gen2-Sammlungen teuer sind und wir daher möchten, dass sie sehr selten vorkommen. Ältere Objekte, die finalisiert werden müssen, müssen möglicherweise auf Dutzende, wenn nicht Hunderte von gen0-Sammlungen warten, bevor ihr Speicherplatz zurückgefordert wird.
Zweitens verursachen Objekte, die finalisiert werden müssen, Kollateralschäden. Da die internen Objektzeiger gültig bleiben müssen, verbleiben nicht nur die Objekte, die direkt finalisiert werden müssen, im Speicher, sondern alles, worauf sich das Objekt direkt und indirekt bezieht, bleibt auch im Speicher. Wenn ein riesiger Baum von Objekten durch ein einzelnes Objekt verankert würde, das finalisiert werden musste, würde der gesamte Baum möglicherweise für eine lange Zeit verweilen, wie wir gerade besprochen haben. Es ist daher wichtig, Finalizer sparsam zu verwenden und sie auf Objekten zu platzieren, die so wenig interne Objektzeiger wie möglich haben. In dem Baumbeispiel, das ich gerade gegeben habe, können Sie das Problem leicht vermeiden, indem Sie die Ressourcen, die finalisiert werden müssen, in ein separates Objekt verschieben und einen Verweis auf dieses Objekt in der Wurzel des Baums behalten.
Schließlich erstellen Objekte, die finalisiert werden müssen, Arbeit für den Finalizer-Thread. Wenn Ihr Finalisierungsprozess komplex ist, wird der einzige Finalizer-Thread viel Zeit damit verbringen, diese Schritte auszuführen. Dies kann zu einem Arbeitsstau führen und dazu, dass mehr Objekte auf die Finalisierung warten. Daher ist es von entscheidender Bedeutung, dass die Finalisierer so wenig Arbeit wie möglich leisten. Denken Sie auch daran, dass, obwohl alle Objektzeiger während der Finalisierung gültig bleiben, diese Zeiger möglicherweise zu Objekten führen, die bereits finalisiert wurden und daher möglicherweise weniger nützlich sind. Es ist im Allgemeinen am sichersten zu vermeiden, Objektzeigern im Finalisierungscode zu folgen, obwohl die Zeiger gültig sind. Ein sicherer, kurzer Finalisierungscodepfad ist der beste.
Nehmen Sie es von jemandem, der in Gen2 Hunderte von MBs nicht referenzierter DataTables gesehen hat: Dies ist äußerst wichtig und wird von den Antworten in diesem Thread völlig übersehen.
Verweise:
1 -
http://msdn.microsoft.com/en-us/library/ms973837.aspx
2 -
http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry
http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage -collector-performance-using-finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/