Laden...

Dispose für jede Klasse implementieren?

Letzter Beitrag vor 16 Jahren 7 Posts 2.371 Views
Hinweis von gfoidl vor 12 Jahren

Abgeteilt von Dispose implementieren und verwenden (IDisposable)

Hallo zusammen,

habe jüngst ein Kollege gesehen, der für JEDE Klasse das IDisposable umgesetzt hat, im Dekonstruktor dementsprechend Dispose aufgerufen hat, und im Dispose den GC um Aufräumen gebeten hat.

Angeblich soll das einen beschleunigten Speicherabbau forcieren.

Ich habe ihm gesagt, solange es sich nicht um unmanged Ressourcen handelt, soll er das sein lassen. Ich gab ihm folgende Begründung mit:
Der GC räumt auf wenn es sein muss. Mag sein, dass ein .NET Programm zunächst mehr Speicher frisst als ein sagen wir C++ Programm. Das liegt nur daran, dass der GC weiß ob es zur Zeit nötig ist Speicher freizugeben oder ob man nicht lieber das Programm arbeiten lassen kann.

Ich stellte dann folgende Regel auf:
Unmanaged Ressourcen werden verwendet: IDisposable umsetzen
Nur Managed Ressourcen: Kein IDisposable

Ich denke damit fährt man ganz gut und liefert die optimale Performance.

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

habe jüngst ein Kollege gesehen, der für JEDE Klasse das IDisposable umgesetzt hat, im Dekonstruktor dementsprechend Dispose aufgerufen hat, und im Dispose den GC um Aufräumen gebeten hat.

Angeblich soll das einen beschleunigten Speicherabbau forcieren.

Das ist totaler Blödsinn, denn wenn man für jede Klasse einen Finalizer implementiert, verbleiben die Objekte länger (!) im Speicher als ohne, da der GC sie nicht direkt abräumen kann, sondern sie erst noch in die Finalizable-Queue stecken muss.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

Hallo Golo,

vielen Dank, ich weiß dass es Unsinn ist, aber nun hab ich auch eine fundierte Begründung warum 😉 (Hab das nicht näher analysiert).

Grüße
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

habe jüngst ein Kollege gesehen, der für JEDE Klasse das IDisposable umgesetzt hat, im Dekonstruktor dementsprechend Dispose aufgerufen hat, und im Dispose den GC um Aufräumen gebeten hat.

Angeblich soll das einen beschleunigten Speicherabbau forcieren.

Das ist totaler Blödsinn, denn wenn man für jede Klasse einen Finalizer implementiert, verbleiben die Objekte länger (!) im Speicher als ohne, da der GC sie nicht direkt abräumen kann, sondern sie erst noch in die Finalizable-Queue stecken muss.

nicht nur das. dadurch arbeitet der GC auch wesentlich langsamer, weil er mehr objekte prüfen muss.

loop:
btst #6,$bfe001
bne.s loop
rts

Ich möchte nochmal das bekräftigen, was Golo zum Finalizer sagte. Der Fehler, einen Finalizer zu implementieren ist einer der häufigsten überhaupt und kann sehr unangenehmen Einfluss auf das Laufzeitverhalten haben. Wenn man also nur Dispose auf den Membern aufrufen möchte, dann keinen Finalizer implementieren sondern nur IDisposable.

Hier ein sehr erschöpfendes Blog:

DG Update: Dispose, Finalization, and Resource Management

Hallo svenson,

auch wenn ein Finalizer Performance kostet, IDisposable ist nach den Richtlinien von MS (und dem Sinn von Dispose nach) nur dann richtig implementiert, wenn Dispose auch aus dem Finalizer aufgerufen wird. Das ist eine feststehende Tatsache. Was anderes ist es, wenn man IDisposable nicht implementiert. Dann sollte man überlegen, ob man überhaupt einen Finalizer braucht. Meistens braucht man ihn dann nicht.

herbivore

IDisposable ist nach den Richtlinien von MS (und dem Sinn von Dispose nach) nur dann richtig implementiert, wenn Dispose auch aus dem Finalizer aufgerufen wird.

Das stimmt, aber nur unter der Vorraussetzung, dass ein Finalizer existiert. Einen Finalizer brauche ich, wenn _unmanaged _Ressourcen direkt verwalte. Mittels IDisposable kann ich die Freigabe _zusätzlich _aktiv machen.

Es macht aber auch in anderen Fällen Sinn, IDisposable zu implementieren, z.B. wenn man nur managed Ressourcen aktiv freigeben will. Hier brauche ich keinen Finalizer, weil die Finalizer bzw. Dispose der Member automatisch aufgerufen werden. Und seit es SafeHandle gibt, ist dieser Fall eigentlich der Standardfall. Und dann sollte eben kein Finalizer implementiert werden. In der Praxis vermutlich selten kritisch, aber wenn man sehr viele Objekte erzeugt, kann sich der unnötige Finalizer sehr unschön auswirken.

Ausnahme von dieser Regel: Man braucht ebenfalls einen Finalizer, wenn die Basisklasse einen Finalizer implementiert.

Beim Umgang mit BS-Ressourcen kann man eigentlich sagen: Für jeden Ressourcentypen eine SafeHandle-Klasse ableiten. Damit kann man auf Finalizer komplett verzichten. IDisposable implementieren, wo aktive Freigabe erwünscht. Bei unmanaged Ressourcen die nicht Zeiger/Handle-basiert arbeiten, muss man in den Finalizer-Apfel beissen.

Hinweis von herbivore vor 12 Jahren

Wenn die Basisklasse einen Finalizer hat, braucht die Unterklasse keinen eigenen zu implementieren, denn der Finalizer (der Oberklasse) macht ja eh nichts anderes als Dispose (false); aufzurufen. Das muss in der Unterklasse nicht nochmal erfolgen. Stattdessen kann die Unterklasse bei Bedarf Dispose (bool) überschreiben.