Ursachen Memoryleaks in .NET


  • Administrator

    Bashar schrieb:

    Dravere schrieb:

    In C# muss man seine Ressourcen selber aufräumen. Das übernimmt der GC nicht.

    Doch. Ich weiß was du meinst, aber die Frage war nach Memoryleaks und nicht nach Ressourcenleaks.

    Und Memory ist keine Ressource? 😉
    Der GC räumt nur rohen Speicher im Hintergrund auf. Die Objekte muss man trotzdem selber freigeben. Das kann z.B. damit passieren, dass man sicherstellt, dass nach einem gewissen Scope einfach keine Referenz mehr auf das Objekt existiert. Man muss halt nicht, wie in C++ ein explizites delete aufrufen. Aber trotzdem musst du dafür sorgen, dass es keine Referenzen mehr hat und somit musst du aufräumen, nicht der GC!

    Grüssli



  • 1. IDisposable aufrufen, wenn ein Typ das Interface implementiert.
    2. Referenzen lösen, wenn das Objekt nicht mehr benötigt wird.

    Der zweite Punkt schließt auch die toten Events mit ein.
    Aber: Er ist er weit weniger schwierig umzusetzen, als es sich anhört und teilweise hier dargestellt wird.

    Hinter einer Referenz auf "hoher" Ebene in der Software können tausende andere Objekte stecken, die sich gegenseitig und Objekte auf tieferen aber auch auf noch höheren Ebenen referenzieren. Löst man die Quellreferenz, ist der Rest aber Freiwild für den GC. Man muss sich bestimmt nicht bei jedem Objekt (strings, anyone?) Gedanken machen, wann und wie man es wieder zur Zerstörung freigeben (also seine Referenzen auf null setzen) muss.

    Dem Gedanken machen stimme ich zu, wenn es um gröbere Architektur und höhere Softwareschichten geht. Auf kleiner Ebene (Granularität: Ein Fenster) kann man verwaltete Objekte i.d.R. erstellen ohne auch nur einen Gedanken daran zu verschwenden wann und wie sie wieder freigegeben werden.



  • Dravere, ich nehme an, dass wir uns nicht über Fakten zu streiten brauchen, nur über die Interpretation. Ist diese Sichtweise für dich irgendwie produktiv? Du holst dir doch damit praktisch das schlechteste aus beiden Welten rein: Kein RAII und keine automatische Speicherfreigabe.


  • Administrator

    @μ,
    Aber den Gedanken, dass die Objekte mit z.B. dem Fenster freigegeben werden, musst du trotzdem noch machen. Ich bin mir jedenfalls meistens bewusst, wie lange meine Objekte gültig bleiben. Und dadurch halte ich an gewissen Orten mit dem Programmieren auch schon mal an, wenn ich mich plötzlich nicht mehr sicher bin, ob die Dinger denn wirklich freigegeben werden können.

    Natürlich wird das auch mit der Zeit zu einer Sache, welche etwas unterbewusst verfolgt wird. Strings sind z.B. eigentlich meistens an ein einzelnes übergeordnetes Objekte gebunden, bzw. wird mit diesem freigegeben.

    @Bashar,
    Ich hole nichts rein. Ich schaue es mir nur so an, wie es tatsächlich ist. Alles andere ist ein sich selbst täuschen und in Sicherheit behaupten. Es ist allerdings auch nicht so schwer und aufwendig, wie das jetzt vielleicht klingen mag. Die Sprache hilft einem bei dieser Aufgabe ja schon, bzw. der GC. Wie μ gesagt hat, kann man die Referenz auf ein Objekt lösen, wodurch ein ganzer Strang freigegeben wird, bzw. der GC erkennt, dass er den Speicher all dieser Objekte freigeben darf. Aber man muss sich trotzdem bewusst sein, wie was und wo zusammenhängt.

    Zudem, der Speicher wird automatisch vom GC freigegeben. Die Objekte aber nicht. Ich bin der Meinung dazwischen sollte man deutlich stärker unterscheiden. Der GC verwaltet nur den Speicher der Objekte, aber nicht die Objekte selbst. Ich muss die Objekte freigeben, vom Hauptprogramm entkoppeln, damit der GC den Speicher der Objekte aufräumen kann. Aber zuerst muss ich als Programmierer tätig werden.

    Wenn man sich das im Hinterkopf behält, kann dies direkt bei der Architektur des Programmes berücksichtig werden. Vielfach löst es sich von selbst, wenn man ein hierarchisches Besitzverhältnis verfolgt. Aber es gibt doch auch immer wieder Situationen, wo es ganz interessant wird. Vor allem auch bei WPF mit den DependencyProperties, allen Events, usw.

    Nur damit es hier nicht falsch verstanden wird, möchte ich das nochmals klar stellen:
    Mir geht es nicht darum, dass man nun bei jedem new zuerst eine Minute lang überlegen soll, wann dieses Objekt freigegeben werden sollte. Mir geht es mehr darum, dass man sich bewusst ist, dass nicht der GC das Aufräumen übernimmt und man somit sowas im Hinterkopf behalten sollte. Man sollte jederzeit in der Lage sein zu jedem Objekt zu sagen, wann es freigegeben wird. Sobald man diese Frage nicht innert ein paar Sekunden beantworten kann, sollte man Anfangen Zeit zu investieren.
    Ich mache das inzwischen völlig automatisch. Es kommt mir vor wie beim Tastaturschreiben. Ich muss gar nicht auf den Bildschirm schauen und plötzlich merke ich, dass ich mich wohl vertippt habe. Gleiches gilt hier, ich merke plötzlich, dass ich mir unsicher über die Lebensdauer eines Objektes bin.

    Grüssli



  • Sorry, je mehr ich drüber nachdenke, desto unsinniger kommt mir diese Philosophie vor.

    Zudem, der Speicher wird automatisch vom GC freigegeben. Die Objekte aber nicht.

    Was soll das bedeuten? Es gibt sowas wie "Objekt freigeben" überhaupt nicht. Das ist begrifflicher Unsinn.

    Das was du so nennst, nämlich Referenzen lösen, betrifft auch überhaupt keine Objekte, sondern Referenzen. Es ist den Objekten sogar völlig gleichgültig, ob du irgendwo Referenzen löst.

    Und zu "Ich hole nichts rein." Doch, hast du selbst geschrieben. Du bist der Meinung, eine hierarchische Besitzstruktur haben zu müssen. Du hast dir also eine künstliche Beschränkung auferlegt. Dabei bringt Hierarchie überhaupt keine Vorteile, ein GC kann mit Zyklen genauso gut umgehen.


  • Administrator

    Bashar schrieb:

    Sorry, je mehr ich drüber nachdenke, desto unsinniger kommt mir diese Philosophie vor.

    Das ist dein gutes Recht 🙂
    Aber meiner Meinung nach überinterpretierst du meine Aussagen.

    Bashar schrieb:

    Und zu "Ich hole nichts rein." Doch, hast du selbst geschrieben. Du bist der Meinung, eine hierarchische Besitzstruktur haben zu müssen. Du hast dir also eine künstliche Beschränkung auferlegt. Dabei bringt Hierarchie überhaupt keine Vorteile, ein GC kann mit Zyklen genauso gut umgehen.

    Hier ist z.B. so eine Überinterpretierung drin. Ich habe nirgends gesagt, dass ich mich auf eine hierarchische Besitzstruktur einschränke. Ich sagte nur, dass diese das Problem meistens automatisch löst. Und schau mal deine Programme an, wieviel hierarchische Besitzstruktur darin vorkommt.
    Ich verwende aber auch andere Arten von Besitzstrukturen. Nur passe ich dann besser auf.

    Wenn du sagst, dass der GC auch mit Zyklen genauso gut umgehen kannst, dann stimme ich dir zwar teilweise zu. Du hast aber dabei eine Überlegung vergessen zu machen. Dieser Zyklus, welcher der GC nun freigibt, wurde von irgendjemandem besessen.

    Ich kann mich nur nochmals wiederholen, überinterpretiere meine Aussagen nicht. Es ist einfach nur ein etwas anderer Denkansatz. Man könnte vielleicht auch sagen, dass ich einfach nur bewusst mit dem GC zusammenarbeite. Und zusammenarbeiten heisst, dass ich mir z.B. auch sehr wohl bewusst bin, dass der GC Zyklen freigeben kann.

    Grüssli



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


Anmelden zum Antworten