Destructor wird aufgerufen, speicher wird trotzdem nicht freigegeben, was ist da los ?



  • Hallo liebe Community, habe schon wieder ein Problem 😕

    Hab eine einfache Linked List in C++ geschrieben, erstellen hinzufügen und löschen sollte funktioniern, der destructor wird auch aufgerufen, aber wenn ich z.B. meine Liste testen will, und sie mit 10.000 Elementen fülle kann ich im Taskmanager beobachten wie der Speicher immer steigt und steigt, was auch normal ist, aber wenn der Anwendungsblock verlassen wird, wo die Liste erstellt wurde, wird der Speicher nicht freigegeben !

    Woran kann das liegen ?? Wie gesagt, der destructor wird von jedem Elemnt aufgerufen, auch von der Basisklasse (dort ist er virtual).

    Freigegeben wird ordnungsgemäß mit delete zeiger; ich verwende Code::Blocks mit mingw, und windows xp prof sp2.



  • Mir ist gerade eingefallen was vielleicht schuld daran sein kann:

    Ich erzeuge die LinkedList in einer Funktion und lasse sie dort auch befüllen, also eine Initialisierung + eine Schleife wo die Liste befüllt wird. Kann es sein das der Compiler die Funktion als Inline verwendet ???

    Dann wäre es klar warum der Speicher im Taskmanager nicht wieder fällt.

    Gibt es eine möglichkeit zu bestimmen ob inline oder nicht ??? (Einige Compiler sind ja schon so klug und entscheiden selbst ob inline oder nicht, und die Angabe von inline ist nur ein Vorschlag für den Compiler, aber kein garant das die Funktion als inline behandlet wird). 😕



  • Was der Taskmanager sagt ist Nebensache. Wenn du im Programmcode den Speicher wieder freigibst, heißt das nicht dass der Speicher auf Betriebssystemebene sofort dem Porgramm wieder weggenommen wird. Das OS ist vermutlich umsichtig genug, um den eben benutzten Speicher erstmal weiterhin dem entsprechenden Prozess zuzuordnen, falls der nochmal für irgendwas Speicher anfordern sollte. Wenn der Destruktor aufgerufen wird und du den Aufruf nicht explizit machst (sondern nur über delete), dann wird der Speicher auch freigegeben. (Okay, vorausgesetzt du benutzt einen standardkonformen Compiler und du oder deine benutzten Bibliotheken haben operator delete nicht überladen)



  • Bist du dir sicher, dass der Destruktor auch alles sauber aufräumt?
    Ohne Code können wir leider nicht viel dazu sagen...



  • Euch ist schon klar, dass new/delete den Speicher nicht jedes mal vom Betriebssystem holen, sondern einen Pool haben, das heißt, dass deine Laufzeitumgebung den Speicher durchaus noch belegt haben kann falls du demnächst wieder ordentlich Speicher brauchst.
    Und so lange es im System noch viel freien Speicher gibt hat diese auch keinen Grund ihn sofort wieder herzugeben.

    Benutze doch mal die Debugging Möglichkeiten deines Compilers, der zeigt dir normalerweise Leaks an.
    Beim MSVC kannst du mal einen Blick in die debug.h werfen, dort gibt es auf jeden Fall eine entsprechende Methode.



  • Danke für die vielen Antworten.

    @Pumuckl
    Dachte dass das OS den Speicher gleich freigibt und man es im Taskmanager sieht, so war es auch bei meinem letzten Programm (war aber in C geschrieben).
    Hört sich jedenfalls logisch an. Danke

    @Nexus
    Der Destructor räumt alles auf, es gibt nämlich nur einen Zeiger, und der wird mit delete gelöscht. Ansonsten enthält ein Knoten keine Daten (bis auf einen Integer) die gelöscht werden müssen.

    @Tippgeber
    Im System gibt es noch genügend Speicher, bei den Tests kam das Programm auf ca. 4000kb Speicher (stand im Taksmanager) beim starte hatte es nur 800 - 900kb.

    Kenne mich mit den Debugging Möglichkeiten von Compilern nicht aus 😕 (benutze die gnu toolchain). Werd mir mal die debug.h von MSVC ansehen 🙂

    Wie überprüft ihr eigentlich eure Programme/Projekte auf Speicherleaks ?? (außer mit den debugging funktionen des Compilers)

    Danke an alle für die Antworten !!



  • Gast4571248 schrieb:

    Wie überprüft ihr eigentlich eure Programme/Projekte auf Speicherleaks ?? (außer mit den debugging funktionen des Compilers)

    Möglichst RAII, smartpointer etc. benutzen und die Speicheranforderungen an möglichst wenig Stellen machen, so sind die Stellen wo überhaupt triviale Speicherlecks auftreten können nur wenige und meist gut manuell zu überprüfen. Komplizierter wirds bei Ringreferenzen, da muss man dann im Design drauf achten dass die nicht auftreten (können) bzw explizit aufgelöst werden.



  • die msvc runtime bietet dafür hilfreiche funktionen an.

    http://msdn.microsoft.com/en-us/library/5at7yxcs.aspx



  • Es gibt spezielle Speicherleck-Detektoren:
    http://search.live.com/results.aspx?q=c%2B%2B+leak+detector



  • Was sind Ringreferenzen ? Hab gegooglt aber nichts gefunden.

    Das was du gesagt hast sind Maßnahmen um sich vor Speicherleaks vorzubeugen, aber ich meinte eher, wenn ihr das Programm testen wollt, ob auch tatsächlich niregends ein Leak ist, wie macht ihr das.

    Auf smartpointer und RAII auszuweichen ist zwar gut, aber da ich C++ noch nicht so gut kann, werd ich erst später darauf ausweichen.

    Hab jetzt mal zu Testzwecken in C ein kleines Programm geschrieben welches nur mit malloc 1024.000 byte anfordert, und danach wieder freigibt, und auch hier sieht man im Taskmanager nur den Speicher ansteigen aber nicht fallen.

    Also das Problem hat sich damit glaub ich erledigt, aber dazugelernt hat man wieder was 🙂

    Sehr tolle/s Community/Forum 👍



  • @letzten 2 Posts

    Danke für die Links, hab geschrieben während ihr gepostet habt.

    Was ich noch bemerkt habe:

    Nachdem free() aufgerufen wird, sieht man im Taskmanager den Speicher um 4 kbyte kleiner werden 😕 wenn ich free weglasse bleibt er so wie er ist (nach malloc).



  • pumuckl schrieb:

    Wenn du im Programmcode den Speicher wieder freigibst, heißt das nicht dass der Speicher auf Betriebssystemebene sofort dem Porgramm wieder weggenommen wird. Das OS ist vermutlich umsichtig genug, um den eben benutzten Speicher erstmal weiterhin dem entsprechenden Prozess zuzuordnen, falls der nochmal für irgendwas Speicher anfordern sollte.

    ich denken, du beschreibst den sachverhalt nicht richtig. das speichermanagment des prozesses arbeitet normalerweise unabhängig von dem des os und ruft dieses nur auf, wenn es neuen speicher haben will/welchen zurückgeben will. aber aus performancegründen nicht unbedingt auf basis von einzelnen freigaben/reservierungen. die modernen speicherlibs hand haben es dann so, dass sie speicherbereiche für kleine objekte in einer gewissenen menge an seiten ablegen. die menge kann wachsen, wird aber nie kleiner, da der verwaltungsaufwand sich im normalfall schlicht nicht lohnt. lediglich größere speicherbereiche werden tatsächlich an das system wieder zurückgegeben. das ist zumindest die funktionweis auf den unixoiden systemen, die ich kenne. im klassischen unix wurde sogar vollständig darauf verzichtet, speicher an das system zurückzugeben.



  • Gast23582538 schrieb:

    Was sind Ringreferenzen ? Hab gegooglt aber nichts gefunden.

    Wenn du über einen Smartpointer ein Objekt referenzierst, wird er das Objekt zerstören, sobald er selber zerstört wird (bzw. sobald der letzte smart pointer zerstört wird der das Objekt referenziert), du musst also nicht explizit delete aufrufen oder dich anderweitig um die Zerstörung des Objektes kümmern. So kannst du z.B. einen Baum aufbauen und nur das root-objekt halten, sobald du es zerstörst wird automatisch alles was daran hängt zerstört.
    Anders ist das, wenn z.B Objekt A eine Referenz auf B hält, B wiederum auf C udn C auf A. Wenn nicht eins der Objekte explizit zerstört wird gibts immer ein anderes Objekt, was darauf eine Referenz hält und die Objekte bleiben im Speicher, selbst wenns von außerhalb des Ringes keine Referenzen mehr darauf gibt. Da solche Ringe zu sehr komplizierten Gebilden anwachsen können, kann es schwierig sein, solche unabhängigen Gebilde zu erkennen und zu zerstören. Sprachen mit Garbage-Collectoren (z.B. Java) können mit sowas Probleme bekommen, zumal dem programmierer dort die Problematik tendenziell weniger bewusst ist, da er sich normalerweise garnicht um Speicherverwaltung kümmern braucht (oder zumindest viel weniger als in C++)



  • Danke, tolle Erklärung !

    Habe jetzt mein Programm mit einem memory leak detector getestet, kein Fehler gefunden 🙂 🙂


Anmelden zum Antworten