wo muss ich nach dem leak suchen?



  • hallo,
    Ich habe meinen Code jetzt mal durch Valgrind laufen zu lassen, dabei ist ein Memory-Leak aufgetreten:

    ==4977== ERROR SUMMARY: 11 errors from 9 contexts (suppressed: 0 from 0)
    ==4977== malloc/free: in use at exit: 112 bytes in 3 blocks.
    ==4977== malloc/free: 91 allocs, 88 frees, 549,228 bytes allocated.
    ==4977== For counts of detected errors, rerun with: -v
    ==4977== searching for pointers to 3 not-freed blocks.
    ==4977== checked 4,575,432 bytes.
    ==4977==
    ==4977==
    [b]==4977== 24 bytes in 1 blocks are definitely lost in loss record 1 of 3
    ==4977==    at 0x4A06FCC: operator new(unsigned long) (in /usr/lib/valgrind/amd64-linux/vgpreload_memcheck.so)[/b]
    ==4977==    by 0x402A38: main (main.cpp:10)
    ==4977==
    ==4977== LEAK SUMMARY:
    ==4977==    definitely lost: 24 bytes in 1 blocks.
    ==4977==      possibly lost: 0 bytes in 0 blocks.
    ==4977==    still reachable: 88 bytes in 2 blocks.
    ==4977==         suppressed: 0 bytes in 0 blocks.
    ==4977== Reachable blocks (those to which a pointer was found) are not shown.
    ==4977== To see them, rerun with: --leak-check=full --show-reachable=yes
    

    Jetzt meine Frage: Was hat es mit der fettgedruckten Zeile auf sich? in main.cpp rufe ich (wie man sieht) nur "new" auf. Wieso wird da aber der Memory-Leak in /usr/lib/valgrind/amd64-linux/vgpreload_memcheck.so angegeben? ist im "new-operator" von Valgrind ein Memory-Leak oder soll das so sein? Was mache ich falsch/wie kann ich jetzt weitersuchen?
    Eigentlich halte ich es nämlich für unwahrscheinlich, dass mein Code leakt, da ich einen Garbage-Collector implementiert habe 😉



  • zeig mal code



  • MMPointer< Dator<int> > testDator = new Dator<int>(testInt); // MMPointer ist mein smartpointer
    
    // das obige hat genau den gleichen effekt (24 Byte in der valgrind-library) wie folgendes:
    Dator<int>* testDator = new Dator<int>(testInt); // hier wird nicht der MMPointer benutzt.
    

    Das eigentlich Seltsame ist jedoch, dass das zweite nicht mehr leakt, wenn ich "delete" aufrufe. Der Pointer "testDator" im zweiten ist bis zum ende bekannt und das betriebssystem müsste das ja dann theoretisch aufräumen.
    Liegt der Fehler in meinem GC?



  • 1. Valgrind ersetzt den operator new. Deshalb taucht das in der Meldung auf.

    2. Du hast einfach den Zeiger aus main.cpp:10 nicht wieder freigegeben. Das ist ganz einfach.



  • SO, problem gelöst!

    Ursache war: Human Error 🙂
    und zwar habe ich zwar einen ganz tollen Garbage-collector implementiert, mit zwei listen, eine mit objekten, die noch benutzt werden, eine andere mit objekten, die nicht mehr benutzt werden. Meine Methode, die dann die aufräumen soll, hat dummerweise die liste mit den noch benutzten objekten gelöscht statt die mit den unbenutzten 🙂 Da ich aber nur ein testobjekt gemacht habe und dieses Objekt unbenutzt war, wurde es nicht gelöscht.
    Die Methode, die dann endgültig aufgeräumt soll (alles löschen zu programmende), hat übrigens auch nur die liste mit den noch benutzten Objekten durchgeschaut, ich weiß echt nicht, was ich mir dabei gedacht habe 🤡 (naja, zu meiner entschuldigung muss man sagen, es war schon recht spät / früh am morgen 😃

    edit: Danke nochmal,
    dass valgrind die speicheroperatoren ersetzt weiß ich zwar, was mich irritiert hatte war bloß, dass das leak in valgrinds library angezeigt wurde und nicht in meinem code.



  • erstmal valgrind leakt nicht. zumindest auf keine weise, die valgrind selber erkennen kann. das der fehler in der valgrind-lib gemeldet wird, hat den einfachen grund, dass der speicher dort reserviert wurde. auf gerufen von deiner main-funktion in zeile 10 der datei main.cpp.

    möglichkeiten voran es liegt, gibt es viele. ohne deinen code wird die analyse allerdings recht schwierig... scheinbar hast du eine problem mit deinem MMPointer. ohne code kann dir aber keiner helfen.

    was deinen normalen pointer angeht: nein, das system räumt nicht auf. sondern gibt den speicher nach beenden des programmes wieder frei. zeiger die zu diesem zeitpunkt noch existieren sind entweder leaks oder gewollte beschleunigungen. letzteres ist der grund für die umfangreichen sammlungen an suppressions für valgrind.



  • ghorst schrieb:

    was deinen normalen pointer angeht: nein, das system räumt nicht auf. sondern gibt den speicher nach beenden des programmes wieder frei. zeiger die zu diesem zeitpunkt noch existieren sind entweder leaks oder gewollte beschleunigungen.

    sind diese "wieder frei"-gegebenen Datenblöcke dann in Valgrind unter "still reachable" aufgelistet?



  • ok. da habe ich mich unglücklich ausgedrückt.
    das muss man unterscheiden: "still reachable" bedeutet, dass man bis zum schluss einen zeiger auf den anfang des speicherbereiches hat. da c++ allerdings am ende der main-funktion noch aufräumt und alle in main() definierten variablen zerstört, trifft das in deinem fall nicht zu und der speicherbereich ist "definitely lost".



  • ok, vielen Dank schonmal 🙂
    Eine frage habe ich aber noch, jetzt, wo ich grad schonmal dabei bin 😃
    Welche Variablen werden nicht zerstört?
    Alle, die nicht in main definiert wurden oder nur spezielle wie z.B. statische?



  • alle die nicht in einer funktion definiert wurden bzw. nicht normal sind. (main ist aus c++ sicht nur eine normale funktion. daher gibt es für c++ auch noch einen exceptionhandler hinter der main.)
    also statischen klassenvariablen, globale variabel und statische lokale variablen (letztere sehen nur so aus, als wenn sie wirklich zu der funktion gehören würden.) werden nicht zerstört.


Anmelden zum Antworten