Resourcenbereinigung .Net

  • Allgemein

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von Gonger96.

    Resourcenbereinigung .Net

    Artentus schrieb:

    Das bedeutet im Klartext, jede dieser Instanzen, die du erstellst, muss irgendwann mal mit Dispose oder mit einem Using-Block freigegeben werden, ansonsten läuft dir dein Speicher zu.

    Als ich über IDisposeable gemeckert hab, hieß es Dispose() wird automatisch im Finalizer aufgerufen. Wenn dem so ist, wird nach zerstören der Instanz auch der unverwaltete Speicher freigegeben. Was ist denn nun richtig?


    Hey,
    ich wundere mich bis heute über IDisposeable. Imho ists absolut unnötig und ich verstehe nicht wieso ich in einer Hochsprache noch Speicher manuell freigeben muss. Normalerweise ist es so konzipiert, dass man im Finalizer den unverwalteten Speicher freigibt und um diesen gezielt eher freigeben zu können (die Instanz liegt ja auf dem verwalteten Heap und wird vom GC irgendwann dynamisch getillt), wird Dispose() aufgerufen. Wobei dies auch im Finalizer getan wird, so wäre eine ein expliziter Aufruf unnötig, wenn man den Speicher nicht früher freigeben wollte. Hätte man nun einen expliziten Aufruf des Destruktors/Finalizers möglich gemacht und entsprechend dann die Entsorgung durch den GC implementiert, wäre das Interface absolut unnötig. Sehe ich das richtig?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

    Wie SpaceyX bereits sagt, es wäre vermutlich am besten, du zeigst uns einfach mal den Code.

    @Gonger96
    Es sollte so sein, ja. Allerdings lässt sich 1. keine genaue Aussage treffen, wann der Finalizer aufgerufen wird und 2. ist es offensichtlich nicht immer möglich/nicht immer so implementiert. Es hat einen Grund, warum man immer Dispose manuell aufrufen sollte. Sollte doch gerade dir als C++-Programmierer geläufig sein, dass man Heapspeicher immer freigibt.

    Artentus schrieb:

    warum man immer Dispose manuell aufrufen sollte


    Oder ggf. einfach die Using/End Using-Anweisung nutzt.
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    In C++ wird jeder Speicher im Destruktor freigegeben. Das ist immer möglich. Da Instanzen sich nicht selbst löschen können gibts dafür noch Smartpointer + Deleter. So kann man entsprechend sagen ob delete, delete[] oder irgendwas anderes z.B. ptr->Release() für COM aufgerufen werden soll. Wenn man da Speicher manuell freigeben muss, hat man was falsch gemacht. Das Problem, zu wissen wann die Instanz bereinigt wird ist vorhanden, da das aufm managed Heap liegt. In C++ liegen Klasseninstanzen im Normalfall auf dem Stack, die werden natürlich fein nach Methodenende freigegeben. Trotzdem dürfte kein Memoryleak entstehen, wenn das ordentlich im FW implementiert wäre.

    Gonger96 schrieb:

    ich wundere mich bis heute über IDisposeable. Imho ists absolut unnötig und ich verstehe nicht wieso ich in einer Hochsprache noch Speicher manuell freigeben muss.
    mir scheint, da hast du was falsch verstanden.
    IDisposable ist normalerweise nicht dazu da, um Speicher freizugeben - das macht der GarbageCollector.
    IDisposable dient der Freigabe anderer Resourcen, mit denen der GC nicht umzugehen weiß.
    Zum Beispiel wenn vom Betriebssystem ein FileHandle angefordert wurde - sowas kann der GC nicht freigeben. Deshalb implementieren zB Streams IDisposable.
    Hi
    es ist allerdings Teil des korrekten Designpatterns, dass man in Finalize alle nicht-verwalteten Ressourcen (Handles, unverwalteten, reservierten Speicher, usw.). Es ist allerdings ebenfalls ein gefordertes Designpattern, dass Ressourcen freigegeben werden, sobald sie nicht mehr benötigt werden. D.h. Dispose sollte aufgerufen werden - kann ja auch verwaltete Dinge betreffen. Außerdem SOLL (heißt nicht, dass man es muss, aber man soll es tun, der Finalizer SOLLTE es außerdem eben trotzdem erledigen, dennoch ist es unsauber, das so zu lösen) man Dispose für jeden die IDisposable-Schnittstelle implementieren Typ aufrufen.

    Gruß
    ~blaze~
    Das ist mir klar, hab ich auch so verstanden. IDisposeable ist dazu da um unverwalteten Speicher/Resourcen freizugeben. Der GC kann (die richtige Implementierung natürlich vorrausgesetzt) so auch unverwaltete Resourcen freigeben, da im Finalizer, der aufgerufen wird, wenn das Objekt durch den GC zerstört wird, Dispose() aufgerufen wird. So werden Memoryleaks komplett ausgeschlossen und ich muss auch nicht Dispose() aufrufen. Also könnte man das Zeugs auch direkt in den Finalizer packen und sich IDisposable sparen.

    Der Vorteil von diesem Interface ist einzig, dass man die Resourcen auch schon vorher freigeben kann. Hätte man den Finalizer als solch genutzt anstatt ein Interface zu nutzen wäre das doch eine viel schönere Lösung gewesen.

    Es geht mir auch nicht wirklich 'drum wie es ist. Mir ist klar, dass bei aktueller Architektur der einzig sinnvolle Weg ist Dispose() aufzurufen, wenns nicht mehr gebraucht wird. Mir geht es eher darum, wie es hätte sein können. So halte ich IDisposable für einen Designfehler, den man besser einzig über den Finalizer hätte lösen können.
    Sieh es als guten Stil an, Dispose dort aufzurufen, wo sichergestellt ist, dass das Objekt nicht mehr weiter benötigt wird. Vorsicht allerdings, die Ressource sollte auch bspw. nicht für andere Klassen verwendbar sein, die ebenfalls Dispose semantisch korrekt aufrufen könnten (d.h. z.B. eine Instanz im Konstruktor erstellen oder die Instanz übergeben werden könnte) oder für Daten, die von mehreren Klassen verwendet werden sollen. Notfalls immer entsprechend wrappen, sodass die Zugehörigkeit ohne böswillige/"dumme" Aktion eines Programmierers immer auf den zurückfällt, der in deinen Augen zuständig ist.
    Ich hoffe, du verstehst diesen Wirrwarr. ^^

    Gruß
    ~blaze~
    Hätte man es über den Finalizer laufen lassen, dann wäre man gezwungen gewesen den Finalizer immer manuell aufzurufen, für jedes Objekt. Das würde aber den GC komplett überflüssig machen. IDisposable ist schöner, weil nur diejenigen Objekte Dispose zur Verfügung stellen, bei denen es auch gebraucht wird, um den Rest kümmert sich der GC automatisch.
    Speicher sollte freigegeben werden, sobald er nicht mehr benötigt wird, nicht, wenn dem GC gerade danach ist.
    Das ist ja auch absolut logisch Speicher dann freizugeben, wenn er nicht mehr benötigt wird und nicht noch auf den GC zu warten. Nur hätte man das imho nicht so lösen sollen.

    @Artentus

    Gonger96 schrieb:

    und entsprechend dann die Entsorgung durch den GC implementiert

    Ich hätte es so geregelt, dass der GC wie gewöhnlich läuft. Nur mit dem Unterschied das Interface rauszuschmeißen. Man könnte den Speicher dann freigeben, wenn er nicht mehr benötigt wird. Das Verhalten wäre genauso wie momentan, nur halt ohne IDisposable.

    /Offtopic
    Es ist doch schon nervig wenn man seinen Beitrag abschickt, dann drei neue gepostet wurden und man direkt editieren darf. Gibt es denn kein Plugin oder Ähnliches, dass anzeigt wenn neue Beiträge geschrieben wurden?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

    Ich finde, MSDN beschreibt das gar nicht schlecht:
    The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams.

    Use the Dispose method of this interface to explicitly release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method when the object is no longer needed.

    Bei verwaltetem Code ist es eine KANN-Option, um den GC zu unterstützen bzw. die Freigabe zu beschleunigen.
    Bei unverwaltetem Code ggf. ein MUSS.

    Gonger96 schrieb:

    Ich hätte es so geregelt, dass der GC wie gewöhnlich läuft. Nur mit dem Unterschied das Interface rauszuschmeißen. Man könnte den Speicher dann freigeben, wenn er nicht mehr benötigt wird.
    Aber wie, wenn du kein Dispose hast?
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „petaod“ ()

    Es geht mir nicht darum ob und wann ich Dispose aufrufen muss, dass ist mir klar. Ich zweifle die Architektur dahingehend an. Man hätte dass, wie oben beschrieben, allein durch den Destruktor lösen sollen. Das wäre 1:1 wie es momentan ist, nur man spart die Schnittstelle.

    Man könnte es sich dann teilweise wie in C++ vorstellen. Dort hat man auch irgendwelche Handel von Windows frei zugeben und Speicher freizugeben. Beides läuft im Destruktor wie schon erklärt. Ich sehe keinen Bedarf das zu trennen, wenn das Objekt zerstört wird, werden auch unverwaltete Ressourcen frei gegeben, andersherum genauso.

    Änderbar ists sowieso nicht, aber hätte MS das so implementiert, wäre es viel schöner für den Programmierer.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

    Je länger ich darüber nachdenke, desto mehr gebe ich dir recht.
    Man hat das einfach von Anfang an so akzeptiert und nie in Frage gestellt.

    Mir fällt auch kein vernünftiges Argument mehr ein.
    Welchen Vorteil hat ein Dispose, das ein Finalize nicht genau so gut erledigen könnte?

    Aber ich mag auch nicht glauben, dass Microsoft-Designer so blöd sind, dass sie extra ein zusätzliches unnützes Interface einbauen.
    Wie sieht's denn bei der threadübergreifenden Verwendung von Objekten aus?
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Man muss Managed und Unmanaged grundsätzlich trennen, deshalb wird das vorallem durch ein extra Interface gelöst. Der GC kann z.B. nicht Wissen, wann es am besten ist diese Unmanaged Ressourcen Freizugeben, es gibt dem Entwickler erstens die Freiheit, die sich ei Cpp Entwickler meistens Wünscht, außerdem kann es sein, dass die Ressource Managed nicht mehr gebraucht wird, Unmanaged jedoch schon.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hallo Gonger96,

    Ich habe jetzt nicht gerade alles durchgelesen, aber an diesem Thema habe ich auch mal zeitweise genagt. Siehe hier. Da warst du doch auch dabei?...

    Grüsse

    Higlav
    Der GC muss auch nicht wissen wann die Ressourcen freizugeben sind, das macht der Programmierer ja. Ich wüsste nicht wieso man managed und unmanaged trennen sollte bis auf

    jvbsl schrieb:

    außerdem kann es sein, dass die Ressource Managed nicht mehr gebraucht wird, Unmanaged jedoch schon.

    Da hab ich nicht drüber nachgedacht. Ist zwar selten der Fall, aber doch ein praktisch Möglicher. Die Lösung ist aber ganz einfach, man gibt die unverwalteten Ressourcen erst dann frei, wenn es gefordert ist. Wenn eine unverwaltete Ressource nun erst bei der letzten Instanz freigegeben werden soll, dann macht man sich halt nen statischen Counter, inkrementiert ihn im Konstruktor, dekrementiert im Destruktor und fragt dann entsprechend ab if(counter == 0) {Ressource freigeben;};. Wäre ja das geringste Problem.