Eine Frage kam während meines aktuellen Kurses auf: Ist das
IDisposable Pattern tatsächlich performanter als einfach nur einen
Finalizer zu definieren?
Ja - bereits bei relativ wenigen Objekten ist der Unterschied
gravierend!
Der Performancevergleich
Zum Vergleich habe ich zwei Klassen definiert, die Eine lediglich
mit einem Finalizer, die Andere mit dem bekannten IDisposable
Pattern. Anschließend erzeuge ich jeweils 2.000 Objekte, setze sie
null bzw. rufe vorher noch Dispose() auf und anschließend stoße ich
den Garbage Collector mittels GC.Collect() an.
Das Ergebnis hat mich dabei selbst fast erstaunt:
Time needed with just the finalizer: 273,8736 ms
Time needed with the disposable pattern: 189,6048 ms
Wenn man die Anwendung mehrfach startet, variieren die Ergebnisse,
manchmal ist auch die Finalizer Variante schneller. Der Grundtenor
ist allerdings: Mit dem IDisposable Pattern ist man im Schnitt gute
150 ms schneller!
Wie funktioniert IDisposable?
IDisposable ist ein in .NET standardmäßig verfügbares Interface,
welches die Methode Dispose() definiert. Diese sollte verwendet
werden um geöffnete Ressourcen vor der Objektzerstörung zu
schließen (Dateien, SQL Verbindungen, etc.). Dadurch erhält man den
Vorteil selbst bestimmen zu können, *wann* der Destruktor-Code
abläuft - den Zeitpunkt der Objektzerstörung kann man trotzdem
nicht beeinflussen.
Um jedoch für den Fall gewappnet zu sein, dass hin und wieder der
Dispose()-Aufruf vergessen wird, sollte man trotzdem einen
Finalizer implementieren, der eine eigene (private) Überladung der
Dispose Methode aufruft. Dieses Dispose hat einen bool Parameter
den man dazu nutzen kann um festzustellen, ob das Dispose() nun
durch einen eigenen Aufruf, oder durch den Finalizer aufgerufen
wurde.
Das Grundproblem des enormen Performanceverbrauchs des Finalizers
liegt in seiner Natur. Der Finalizer (in anderen Sprachen auch
Destruktor genannt) wird dann aufgerufen wenn das betreffende
Objekt zerstört wird. Da in .NET der Garbage Collector dafür
verantwortlich ist und dieser mit einer sehr hohen Priorität
abläuft, läuft auch der Finalizer Code mit dieser Priorität.
Zusätzlich zu diesem Performanceverlust kommt noch eine
Besonderheit in der Implementierung im Garbage Collector, die die
ganze Sache noch etwas verlangsamt (damit möchte ich euch vorerst
nicht langweilen
).
Code, et cetera
Ich habe das Testprogramm unter http://github.com/picaschaf/DotNet-Experimental-Collection
veröffentlicht und freue mich über alle
Downloads/Kommentare/Erweiterungen.
Schöne Grüße und viel Spaß beim Einsatz des IDisposable
Patterns,
Alexander Nassian