Ursachen Memoryleaks in .NET



  • Ich mache mir in .NET ehrlich gesagt nur selten Gedanken über Speicher als Resource. Alles was geht, wird in using gepackt und das Thema ist damit abgehakt. Der Rest ist meistens eh nur lokal im Scope.



  • GPC schrieb:

    Ich mache mir in .NET ehrlich gesagt nur selten Gedanken über Speicher als Resource. Alles was geht, wird in using gepackt und das Thema ist damit abgehakt. Der Rest ist meistens eh nur lokal im Scope.

    Oh du Glücklicher...für mich war ein GC bisher praktisch immer mehr Bürde als sonst was...



  • Hmm, bedeutet dies, dass im folgender Situation:

    void Scope()
    {
       var tmp = new MemoryStream(..);
    
       ........
    }
    

    das MemoryStream Objekt zwar am ende eine NULL Referenz hat, da der Scope verlassen wird, aber durch NICHT aufrufen von Dispose, das Objekt nich freigegeben wird?

    GRüße



  • Hier existiert ein gros

    NullBockException schrieb:

    das MemoryStream Objekt zwar am ende eine NULL Referenz hat, da der Scope verlassen wird, aber durch NICHT aufrufen von Dispose, das Objekt nich freigegeben wird?
    GRüße

    Das Objekt wird auch nicht freigegeben, sondern die Resourcen die das Objekt verwaltet.



  • Kommt drauf an.
    Es kann sein dass nichtverwalteter Speicher oder eine Betriebssystemressource nicht freigegeben wird oder etwas anderes nicht sauber aufgeräumt wird. Beispiel: Ein Wrapper für Bitmaps der die Pixel im Konstruktor lockt und in der Dispose-Methode wieder freigibt.

    Kann praktisch alles mögliche sein. Deshalb: Der Entwickler einer Klasse die IDisposable implementiert, hat sich etwas dabei gedacht und deshalb sollte Dispose immer wenn angeboten aufgerufen werden.



  • das bedeutet? sorry versteh ich nich !



  • NullBockException schrieb:

    das bedeutet? sorry versteh ich nich !

    Was verstehst du nicht?



  • NullBockException schrieb:

    das bedeutet? sorry versteh ich nich !

    IDisposable da? myObject.Dispose() schreiben.
    IDisposable nicht da? Eierschaukeln.



  • Das bedeutet, dass Garbage Collection dich vor Memory Leaks schützt. Und der Preis dafür ist, dass jede andere Art von Ressource praktisch unmöglich zu verwalten wird...



  • Hmm ok verstehe, schön wäre es, wenn der ReSharper mir die Stellen iwürde, an denen ich ein mögliches Dipsose nich aufgerufen habe:)





  • Funktioniert offenbar aber nur für lokale Disposable Objekte und die sind kaum ein Problem, weil da kann man using machen und gut ist. Lustig wirds mit Disposable Membern...



  • dot schrieb:

    Lustig wirds mit Disposable Membern...

    Die sind oft ein guter Hinweis, dass die enthaltende Klasse ebenfalls IDisposable implementieren sollte. In solchen Fällen zieht es sich bis zur Wurzel und ist kein Problem mehr.



  • Ich sehe auch nicht, wieso Member, die man disposen muss, ein Problem darstellen. Dann geht man halt hin und spendiert seiner Klasse selbst Dispose, wo man dann wiederum das Dispose der Member aufruft. Klingt gut. Hab ich was übersehen?



  • 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.

    Es geht hier allein um den Zeitpunkt wann Disposed werden soll. Auch hat es nur indirekt mit unmanaged Resourcen zu tuen. Relevant ist Dispose für stark begrenzte Resourcen. Klar sind alle Resourcen in einem Computer endlich, aber manche sind halt so stark begrenzt das man sie so schnell wie möglich wieder freigeben will sobald man sie nicht mehr braucht. (z.B. Filehandles, Grafikhandles etc)

    Und genau hier steht das Grundprizip der GC im Widerspruch, weil Objekte "irgendwann" abräumt werden. Viele unterliegen der Fehlannahme das eine GC Objekte abräumt wenn die nicht mehr gebraucht werden. Das stimmt aber so nicht. Objekte, die nicht mehr gebraucht werden räumt die GC erst dann ab wenn sie nicht mehr genug Freispeicher hat. Das kann zu einem sehr späten Zeitpunkt passieren.

    Gerade für stark begrenzte Resouecen ist das aber sehr schlecht, weil hier für einen beliebigen, unbestimmbaren Zeitraum dann eine Resource gebunden bleibt obwohl sie längst nicht mehr gebraucht würde.

    Um dieses Problem zu lösen gibt es das Dispose-Pattern. Mit Dispose() kann der Programmierer den Zeitpunkt bestimmen zu dem die begrenzten Resourcen eines Objekts freigegeben werden. Das Objekt selber wird danach immer noch von der GC abgeräumt zu einem beliebigen, späteren Zeitpunkt.

    Natürlich stimmt es also das man Dispose immer aufrufen sollte, aber nicht, weil man sonst ein Resourceleak hätte, sondern weil man sonst eher in Probleme mit begrenzten Resourcen laufen kann.



  • 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.



  • Sprich, der GC ruft die Dispose funktion auf, wenn sie nich schon aufgerufen wurde, allerdings nich deterministisch zu den object/reference scops!?



  • Hab gerade mal hier geschaut:
    http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf

    Punkt 17.12 Finalizer. Da hab ich nichts dergleichen gefunden. loks, hast du da andere Quellen?



  • Es stimmt auch imho nicht, dass der vom Compiler generierte Finalizer Dispose automatisch aufruft.
    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!

    Warum raucht folgendes nach ein paar hunder Iterationen ab (wobei ich unterstelle, dass der Finalizer von Bitmap korrekt Dispose aufruft)?

    for (int i = 0; i < 100000; ++i)
    {
     Bitmap b = new Bitmap(@"C:\xxx.bmp");
     Console.WriteLine(i);
    }
    

    Der nicht-verwaltete Speicher (oder eine andere Ressource) geht zur Neige, der GC weiß davon aber nichts, kann die Finalizer nicht triggern wordurch Dispose nicht aufgerufen wird.


  • Administrator

    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.

    Nö, steht nicht, weil es nicht stimmt. Aber es gibt ein Pattern, welchem man folgen sollte, wenn man unmanaged Ressourcen verwaltet:

    class MyClass
      : IDisposable
    {
      private bool m_isDisposed;
    
      ~MyClass()
      {
        Dispose(false);
      }
    
      protected virtual void Dispose(bool disposing)
      {
        if(!m_isDisposed)
        {
          if(disposing)
          {
            // auch managed ressourcen freigeben
          }
    
          // unmanaged ressourcen freigeben
    
          m_isDisposed = true;
        }
      }
    
      public void Dispose()
      {
        Dispose(true);
      }
    }
    

    Edit: http://msdn.microsoft.com/en-us/library/vstudio/b1yfkh5e.aspx

    Grüssli


Anmelden zum Antworten