wake-up-neo.com

Sollte ich DataSet und DataTable entsorgen?

DataSet und DataTable implementieren beide IDisposable. Daher sollte ich bei herkömmlichen Best Practices ihre Dispose () - Methoden aufrufen.

Nach allem, was ich bisher gelesen habe, verfügen DataSet und DataTable über keine nicht verwalteten Ressourcen. Deswegen macht Dispose () nicht viel.

Außerdem kann ich using(DataSet myDataSet...) nicht einfach verwenden, da DataSet eine Sammlung von DataTables enthält.

Um sicher zu gehen, muss ich also durch myDataSet.Tables iterieren, alle DataTables und dann das DataSet entsorgen.

Lohnt es sich also, Dispose () für alle meine DataSets und DataTables aufzurufen?

Nachtrag:

Für diejenigen von Ihnen, die der Meinung sind, dass DataSet nicht verfügbar sein sollte: Im Allgemeinen verwenden Sie using oder try..finally, um zu gewährleisten, dass Dispose () aufgerufen wird.

Dies wird jedoch für eine Sammlung schnell hässlich. Was tun Sie beispielsweise, wenn einer der Aufrufe von Dispose () eine Ausnahme auslöst? Schlucken Sie es (was "schlecht" ist), damit Sie das nächste Element weiter entsorgen können?

Oder schlagen Sie vor, dass Sie einfach myDataSet.Dispose () aufrufen und vergessen, die DataTables in myDataSet.Tables zu entsorgen? 

183
mbeckish

Im Folgenden werden einige Diskussionen erläutert, warum Dispose für ein DataSet nicht erforderlich ist.

Entsorgen oder nicht entsorgen? :

Die Dispose-Methode in DataSet existiert NUR aufgrund von Nebeneffekten der Vererbung - mit anderen Worten, sie macht beim Finalisieren nichts sinnvolles.

Sollte Dispose für DataTable- und DataSet-Objekte aufgerufen werden? enthält einige Erklärungen von einem MVP: 

Der Namespace system.data (ADONET) enthält nicht nicht verwaltete Ressourcen. Daher ist es nicht erforderlich, diese als .__ abzulegen. solange Sie sich nichts Besonderes hinzugefügt haben.

Verstehen der Dispose-Methode und der Datensätze? hat einen Kommentar von Scott Allen: 

In der Praxis entsorgen wir ein DataSet selten, weil es wenig Nutzen bringt. "

Daher besteht der Konsens darin, dass derzeit gibt es keinen guten Grund, Dispose für ein DataSet aufzurufen.

134
DOK

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 für den Punkt, an dem das Üben nicht sein sollte an der Oberfläche akzeptiert, ohne dass ein genaues, tiefgreifendes Verständnis noch besteht.

Es stellt sich jedoch heraus, dass DataSets, DataViews, DataTables die Finalisierung in ihren Konstruktoren unterdrücken - aus diesem Grund führt das Aufrufen von Dispose () für sie explizit zu nichts.

Vermutlich geschieht dies, weil sie keine nicht verwalteten Ressourcen haben. Obwohl 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, die Finalisierung auf den Datentypen zu unterdrücken, die normalerweise den meisten Speicher belegen, spricht für die Bedeutung dieser Vorgehensweise im Allgemeinen für finalisierbare Datentypen.)

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 ist manchmal frustrierend, liefert aber ein vollständigeres Verständnis für die Rahmenbedingungen, auf die wir uns täglich verlassen.

Nach vielem Lesen, hier ist mein Verständnis:

Wenn ein Objekt finalisiert werden muss, kann es länger als nötig Speicher belegen ) Jeder Typ, der einen Destruktor definiert (oder von einem Typ erbt, der einen Destruktor definiert), wird als finalisierbar betrachtet. b) Bei der Zuweisung (bevor der Konstruktor ausgeführt wird) wird ein Zeiger auf die Finalization-Warteschlange gestellt. 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 (an und für sich) in der Finalisierungswarteschlange befinden. Zu wissen, welche Objekte sich in der Finalisierungswarteschlange befinden und noch finalisiert werden müssen, ist hilfreich (gibt es einen Befehl dafür?)

Durch das Unterdrücken der Finalisierung wird im Header des Objekts ein Bit deaktiviert, das der Laufzeit angibt, dass der Finalizer nicht aufgerufen werden muss (die FReachable-Warteschlange muss nicht verschoben werden). Es verbleibt in der Finalization-Warteschlange (und wird weiterhin von! FinalizeQueue in SOS gemeldet).

Die Klassen DataTable, DataSet und DataView sind alle auf MarshalByValueComponent verwurzelt, 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, aber es befreit den Anrufer von der Sorge, nach der Verwendung Entsorgen anrufen zu müssen
  • Dies und die Tatsache, dass DataTables möglicherweise von verschiedenen DataSets gemeinsam genutzt werden können, ist wahrscheinlich der Grund, warum DataSets nicht darauf achten, untergeordnete DataTables zu entsorgen
  • Dies bedeutet auch, dass diese Objekte in SOS unter! FinalizeQueue angezeigt werden
  • Diese Objekte sollten jedoch nach einer einzelnen Sammlung wieder verfügbar sein, wie ihre nicht finalisierbaren Gegenstücke .

4 (neue Referenzen):

Ursprüngliche Antwort:

Es gibt viele irreführende und im Allgemeinen sehr schlechte Antworten auf diese Frage - jeder, der hier gelandet ist, sollte den Lärm ignorieren und die nachstehenden Hinweise sorgfältig lesen.

Ohne Zweifel sollte Dispose für alle finalisierbaren Objekte aufgerufen werden .

Datentabellen sind finalisierbar.

Das Aufrufen von Dispose beschleunigt die Speicherfreigabe erheblich .

MarshalByValueComponent ruft GC.SuppressFinalize (this) in seiner Dispose () auf - Wenn Sie dies überspringen, müssen Sie auf Dutzende oder sogar Hunderte von Gen0-Sammlungen warten, bevor der Speicher wieder freigegeben wird:

Mit diesem Grundverstä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. Daher wird es erst wieder erfasst, wenn die nächste Gen2-Erfassung erfolgt. Das könnte in der Tat eine sehr lange Zeit dauern, und wenn die Dinge gut laufen, wird es in der Tat eine lange Zeit dauern, da Gen2-Sammlungen teuer sind und daher nur sehr selten stattfinden sollen. Ältere Objekte, die finalisiert werden müssen, müssen möglicherweise auf Dutzende oder gar Hunderte von gen0-Sammlungen warten, bevor ihr Speicherplatz freigegeben 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 müsste, würde der gesamte Baum möglicherweise lange verweilen, wie wir gerade besprochen haben. Es ist daher wichtig, Finalizer sparsam zu verwenden und auf Objekten zu platzieren, die über möglichst wenige interne Objektzeiger verfügen. In dem Baumbeispiel, das ich gerade gegeben habe, können Sie das Problem leicht vermeiden, indem Sie die zu finalisierenden Ressourcen auf ein separates Objekt verschieben und einen Verweis auf dieses Objekt im Stamm des Baums behalten. Mit dieser bescheidenen Änderung würde nur das eine Objekt (hoffentlich ein schönes kleines Objekt) verweilen und die Finalisierungskosten werden minimiert.

Objekte, die finalisiert werden müssen, erzeugen schließlich 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 einen Arbeitsstau verursachen und dazu führen, dass mehr Objekte auf die Finalisierung warten. Daher ist es von entscheidender Bedeutung, dass Finalizer so wenig wie möglich arbeiten. 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, Objektzeigern im Finalisierungscode nicht zu folgen, obwohl die Zeiger gültig sind. Ein sicherer, kurzer Finalisierungscode-Pfad ist der beste.

Nehmen Sie es von jemandem, der 100 MB nicht referenzierter DataTables in Gen2 gesehen hat: Dies ist enorm wichtig und wird von den Antworten in diesem Thread völlig übersehen.

Referenzen:

1 - http://msdn.Microsoft.com/en-us/library/ms973837.aspx

2 - http://vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entryhttp://www.dotnetfunda.com /articles/article524-net-best-practice-no-2-improve-garbage-collector-performance-using-finalizedispose-pattern.aspx

- http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/

123
Nariman

Sie sollten davon ausgehen, dass es etwas Nützliches leistet und Dispose anruft, auch wenn es aktuell nichts tut. Es gibt keine Garantie, dass NET Framework in zukünftigen Versionen so bleibt, was zu einer ineffizienten Ressourcennutzung führt.

22
Nuno

Selbst wenn das Objekt über keine nicht verwalteten Ressourcen verfügt, kann das Entsorgen der GC helfen, indem Objektdiagramme gebrochen werden. Im Allgemeinen sollte Dispose () aufgerufen werden, wenn das Objekt IDisposable implementiert.

Ob Dispose () tatsächlich etwas tut oder nicht, hängt von der angegebenen Klasse ab. Im Falle von DataSet wird die Dispose () - Implementierung von MarshalByValueComponent übernommen. Es entfernt sich aus dem Container und ruft das Disposed-Ereignis auf. Der Quellcode ist unten (mit .NET Reflector demontiert):

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this)
        {
            if ((this.site != null) && (this.site.Container != null))
            {
                this.site.Container.Remove(this);
            }
            if (this.events != null)
            {
                EventHandler handler = (EventHandler) this.events[EventDisposed];
                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
    }
}
16
dwieczor

Erstellen Sie die DataTables selbst? Da das Durchlaufen der untergeordneten Objekte eines Objekts (wie in DataSet.Tables) normalerweise nicht erforderlich ist, ist es die Aufgabe des übergeordneten Elements, alle untergeordneten Elemente zu entsorgen.

Im Allgemeinen lautet die Regel: Wenn Sie es erstellt haben und IDisposable implementiert ist, geben Sie es ab. Wenn Sie es NICHT erstellt haben, entsorgen Sie es NICHT. Dies ist die Aufgabe des übergeordneten Objekts. Für jedes Objekt gelten jedoch möglicherweise besondere Regeln. Überprüfen Sie die Dokumentation.

Für .net 3.5 heißt es explizit "Entsorgen Sie es, wenn Sie es nicht mehr verwenden", also würde ich das tun.

7
Michael Stum

Ich nenne es, wenn ein Objekt IDisposeable implementiert. Es ist für einen Grund da. 

DataSets können große Speicherfresser sein. Je früher sie zur Reinigung markiert werden können, desto besser.

Update

Es ist 5 Jahre her, seit ich diese Frage beantwortete. Ich stimme meiner Antwort immer noch zu. Wenn es eine Dispose-Methode gibt, sollte diese aufgerufen werden, wenn Sie mit dem Objekt fertig sind. Die IDispose-Schnittstelle wurde aus einem bestimmten Grund implementiert.

6
Chuck Conway

Wenn Ihre Absicht oder der Kontext dieser Frage wirklich eine Garbage Collection ist, können Sie die Datasets und Datatables explizit auf Null setzen oder das Schlüsselwort using verwenden und sie außer Reichweite lassen. Dispose macht nicht viel, wie Tetraneutron es früher gesagt hat. GC erfasst Datensätze, auf die nicht mehr verwiesen wird, und solche, die außerhalb des Gültigkeitsbereichs liegen.

Ich wünschte wirklich, dass SO die Leute dazu gezwungen hat, einen Kommentar abzugeben, bevor sie die Antwort ablehnen.

4
Srikar Doddi

Datensätze implementieren IDisposable durch MarshalByValueComponent, das IDisposable implementiert. Da Datensätze verwaltet werden, hat der Aufruf von dispose keinen wirklichen Nutzen.

1
Tetraneutron

Keine Notwendigkeit zu entsorgen () Da DataSet die Klasse MarshalByValueComponent erbt, implementieren Sie die IDisposable Interface 

0

Versuchen Sie die Clear () - Funktion zu verwenden. .__Das funktioniert gut für mich, wenn ich es entsorgen möchte.

DataTable dt = GetDataSchema();
//populate dt, do whatever...
dt.Clear();
0
Hasan Savran