Ursachen Memoryleaks in .NET
-
Bashar schrieb:
dot schrieb:
GPC schrieb:
Bzgl. des vorgestellten Patterns möchte ich noch sagen, dass man das nur braucht, wenn man tatsächlich unmanaged resources hat. Hat man nur managed resources, braucht man a) keinen Finalizer und b) kein Dispose(bool)
Sobald du auch nur eine Datei öffnest, hast du schon unmanaged Ressourcen, denn alles außer Speicher am GC Heap ist eine unmanaged Ressource...
Nein, das ist anders gemeint. Es ist überflüssig, in deinem Finalizer deine Managed Resourcen disposen zu wollen, die werden nämlich auch gerade finalisiert. Unmanaged Resourcen haben aber keine Finalizer, die musst du tatsächlich in deinem Finalizer freigeben.
So habe ich es gemeint, ja. Nur dass es nicht nur überflüssig, sondern auch problematisch wäre, denn evtl. disposed man dann managed resources, die bereits finalized wurden.
-
Firefighter schrieb:
loks schrieb:
Hier scheint ein falsches Verständnis von Dispose vorzuliegen. Unabhängig davon ob man nun using benutzt oder Dispose manuell aufruft ist garantiert das Dispose ausgeführt wird, weil dieses im Finalizer sichergestellt wird. Wurde es vorher nicht manuell aufgerufen wird Dispose spätestens im Finalizer ausgeführt.
Das wusste ich noch nicht, steht das im Standard oder gibts da eine Doku zu? Wuerde das gerne nachlesen.
http://msdn.microsoft.com/en-us/library/aa720161(v=vs.71).aspx
Dispose(bool disposing) executes in two distinct scenarios. If disposing equals true, the method has been called directly or indirectly by a user's code and managed and unmanaged resources can be disposed. If disposing equals false, the method has been called by the runtime from inside the finalizer and only unmanaged resources can be disposed. When an object is executing its finalization code, it should not reference other objects, because finalizers do not execute in any particular order. If an executing finalizer references another object that has already been finalized, the executing finalizer will fail.
The base class provides a Finalize method or destructor as a safeguard in the event that Dispose is not called. The Finalize method calls the Dispose method that takes parameters, passing false. You should not re-create Dispose clean-up code within the Finalize method. Calling Dispose(false) is optimal for code readability and maintainability.
-
µ schrieb:
Es stimmt auch imho nicht, dass der vom Compiler generierte Finalizer Dispose automatisch aufruft.
Siehe MSDN link, also nach meinem Verständnis des Textes steht da genau das. Finalize ruft immer Dispose auf als Fallback.
µ schrieb:
Nur wenn der Entwickler der Klasse den Finalizer gemäß dem Dispose-Pattern korrekt implementiert, greift der Notmechanismus. Aber auch darauf darf man sich nicht verlassen!
Naja, das gilt aber für so ziemlich alles was wir benutzen. Man könnte das auf die Grunsaussage reduzieren: Wenn der Programmierer einen Fehler gemacht hat, dann geht etwas schief... Aber das gilt allgemein und nicht nur beim Dispose.
µ schrieb:
Warum raucht folgendes nach ein paar hunder Iterationen ab (wobei ich unterstelle, dass der Finalizer von Bitmap korrekt Dispose aufruft)?
Dann versuch mal diese Variante:
for (int i = 0; i < 100000; ++i) { Bitmap b = new Bitmap(@"C:\Media\Neuer Ordner\Untitled 1.jpg"); Console.WriteLine(i); if (i % 100 == 0) { Console.WriteLine("GC"); GC.Collect(); } }
Worum es mir ging ist, das ein fehlendes Dispose kein Resource-Leak erzeugt. Auch das Beispiel mit den Bitmaps ist kein Resource-Leak, denn die Resourcen können ja immer noch freigegeben werden. Leak bedeutet nach meiner Meinung, daß der Zugriff auf eine Resource verloren geht bevor diese freigegeben wurde und dadurch ein Freigeben unmöglich wird.
Das Beispiel mit den Bitmaps zeigt nur eine Schwäche der GC auf wenn es um unmanaged Resourcen geht, bzw. es zeigt das die GC nur Managed Memory beachtet. Gerade die Bitmaps sind aber eine Klasse wo im Grunde nur ein Manager Wrapper um ein ansonsten unmanaged Objekt rum geschrieben wurde. Der Speicher dieser Bitmaps ist komplett unmanaged und triggert daher überhaupt keine GC.
-
loks schrieb:
Siehe MSDN link, also nach meinem Verständnis des Textes steht da genau das. Finalize ruft immer Dispose auf als Fallback.
Nope, passiert nicht automatisch. Kann auch nirgends eine entsprechende Erwähnung in dem von dir verlinkten Artikel finden. Und sogar im Beispielcode wird gezeigt, wie im Finalizer die Methode Dispose aufgerufen wird. Das ist ein Pattern, welches du befolgen sollst. Da wird nichts automatisch gemacht.
Weitere Informationen findest du an den folgenden Stellen:
Object.Finalize
IDisposable.Dispose
Implementing Finalize and Dispose to Clean Up Unmanaged Resourcesloks schrieb:
Worum es mir ging ist, das ein fehlendes Dispose kein Resource-Leak erzeugt. Auch das Beispiel mit den Bitmaps ist kein Resource-Leak, denn die Resourcen können ja immer noch freigegeben werden. Leak bedeutet nach meiner Meinung, daß der Zugriff auf eine Resource verloren geht bevor diese freigegeben wurde und dadurch ein Freigeben unmöglich wird.
Es ist aber ein Resource Leak.
Zitat aus dem letzten der oben verlinkten Artikel:
MSDN schrieb:
Note that even when you provide explicit control using Dispose, you should provide implicit cleanup using the Finalize method. Finalize provides a backup to prevent resources from permanently leaking if the programmer fails to call Dispose.
Du musst also einen Finalizer implementieren. Sonst hast du ein Resource Leak.
Wobei ich mir gerade nicht sicher bin, ob wir einander vorbeireden. Du redest immer von "automatisch aufgerufen", aber das ist entweder falsch oder völlig falsch formuliert. Meinst du womöglich, dass es von der Implementierung (z.B. von
Bitmap
) sichergestellt ist, dassDispose
im Finalizer aufgerufen wird? Dann würde ich dir nämlich zustimmen.Ich würde es allerdings immer noch als halbes Resource Leak ansehen. Aber die Definition dürfte sich hier dann in einer Grauzone bewegen.
Grüssli
-
loks schrieb:
The base class provides a Finalize method or destructor as a safeguard in the event that Dispose is not called. The Finalize method calls the Dispose method that takes parameters, passing false. You should not re-create Dispose clean-up code within the Finalize method. Calling Dispose(false) is optimal for code readability and maintainability.
Wir drehen uns glaube ich im Kreis... ruft man Dispose nicht selber direkt oder indirekt auf, wird Dispose nur ausgeführt, wenn man das Dispose(false) auch im Finalizer aufruft. Lässt man den Finalizer leer oder unimplementiert, wird der GC auch niemals Dispose aufrufen. Und wir erinnern uns: Das Dispose-Pattern mit Dispose(bool) muss man selbst coden und dementsprechend auch den Aufruf in den Finalizer packen.
-
Dravere schrieb:
Wobei ich mir gerade nicht sicher bin, ob wir einander vorbeireden. Du redest immer von "automatisch aufgerufen", aber das ist entweder falsch oder völlig falsch formuliert.
Ich lag falsch. Wobei ich das schon zig-fach implementiert habe und es auch brav korrekt anwende
Ich denke mal die Altersdemenz macht sich langsam bemerkbar.