Wie oft unterläuft euch ein Memleak?



  • cooky451 schrieb:

    Jedenfalls hat man alle Möglichkeiten die ein GC hat, außer Speicher umräumen.

    Wer sagt denn sowas? ^^

    Du musst lediglich statt Pointer Java-Artige-Referenzen verwenden.



  • Garbage Collection ist imo ein fundamentaler Designfehler ... Ich kenn kein vernünftiges Argument ...

    Hmm, wenn ich an (funktionale) Sprachen denke im allgemeinen und an echte Continuations im speziellen (nicht setjmp/longjmp)... also ich wuerde das so nicht unterschreiben. Ohne Garbage Collector wohl die Hoelle auf Erden. Ich kenne keine halbwegs funktionale Sprache ohne Garbage Collector. Auch der Vergleich mit dem Userinterface ... hmm ... Nein!



  • Xin schrieb:

    hustbaer schrieb:

    Natürlich gibt es auch mit RAII Fälle wo die Dinge anfangen etwas kompliziert zu werden. Diese Fälle sind aber nach meiner Erfahrung ausreichend selten.

    Bäume? Listen?

    Sehe ich jetzt nicht als Problem.
    Vielleicht verstehst du unter RAII auch was anderes als ich. Wenn ich RAII schreibe meine ich bloss dass jede Resource bzw. jedes dynamisch erzeugte Objekt zu jedem Zeitpunkt von genau einem anderen Objekt "kontrolliert" wird, und dieses sich im Destruktor um die Freigabe kümmert.
    Das muss nicht heissen dass die Besitzverhältnisse nicht wechseln können, oder dass ein Objekt nur genau ein anderes kontrollieren darf.

    Bei Listen besitzt z.B. einfach die Liste alle Nodes. Wenn man Nodes "ausklinken" will kann man sie entweder ala std::list in eine neue Liste raustrennen, oder einfach nen std::unique_ptr (bzw. vor C++11 einen std::auto_ptr) zurückgeben.

    Bei Bäumen kann entweder der Baum alle Nodes besitzen, oder der Baum besitzt die Root-Node, und alle Nodes besitzen ihre unmittelbaren Kinder.

    Klar muss man dabei aufpassen während man die Liste oder den Baum selbst implementiert, aber bei der Verwendung muss man sich dann keine Sorgen mehr machen dass irgendwas nicht oder doppelt freigegeben werden könnte.

    Und das ist für mich in Summe dann "ausreichend selten". Zumindest ich implementiere wesentlich weniger oft Datenstrukturen als ich sie dann verwende. (Und meist komme ich sowieso mit den vorgegebenen Datenstrukturen der STL bzw. Boost aus.)

    hustbaer schrieb:

    Wenn ich hin und wieder gut aufpassen muss was ich mache, ist das für mich kein echtes Problem. Wenn ich dagegen bei jeder Zeile aufpassen muss wie ein Haftelmacher ermüde ich viel zu schnell, und fange dann an Fehler zu machen. Und/oder vergleichsweise sehr sehr langsam zu werden.

    Dafür fängt man ggfs. aber an, viel zu kopieren, z.B. wenn man Objekte dann in Vectoren packt.

    Was manchmal auch vollkommen OK ist, weil 80/20 und so.
    Bei den "20% des Codes" Stellen muss man dann halt wieder sein Hirn einschalten wenn das viele Kopieren zu einem Problem wird. Aber auch nicht bei jeder Zeile Code, siehe oben.

    hustbaer schrieb:

    Und was mein "seit RAII quasi keine Leaks mehr" Statement angeht: das ist einfach ne Beobachtung. Meine Konzentrationsfähigkeit ist heute eher schlechter als noch vor 10 Jahren. Wenn ich trotzdem heute deutlich weniger Leaks produziere als damals, kann es also fast nur daran liegen dass ich heute irgendwas grundsätzlich anderes mache. z.B. andere Patterns/Idioms verwenden.

    Okay, das ist eine subjektive Erfahrung (die ich zwar in ähnlicher Form teile), aber halt keine Garantie.

    Ja, klar. Im Endeffekt sind das doch alles nur subjektive Erfahrungen bzw. Meinungen.



  • cooky451 schrieb:

    Ethon schrieb:

    Die einzige Existenzberechtigung von GCs ist doch, dass sie unter Umständen schneller sein können. Zb wenn es viele kleine Allocations gibt die aufgeräumt werden müssen, zb in Java.

    Das kannst du in C++ ganz leicht global (Allokationsoperatoren überschreiben), oder lokal (eigene Allokatoren nutzen) kontrollieren. Und das genau abgestimmt auf deine Wünsche. (...)

    Wenn man in C++ leicht irgendwas sinnvolles machen kann, indem man global operator new/delete überschreibt, dann hat die C++ Implementierung die man verwendet einen furchtbar miesen Allokator.
    Und lokal ist das auch nur in wenigen Fällen sinnvoll möglich.

    Was in C++ z.B. teuer ist, und kaum nicht-teuer zu bekommen, ist thread-safe shared ownership.
    Man kann oft drum rumarbeiten, sein Programm anders designen so dass man keine thread-safe shared ownership mehr braucht. Wenn man es aber nicht kann, dann kann eine GC-Implementierung hier schon die Nase vorn haben. Ein klassischer "stop the world" Collector erlaubt nämlich ganz stinknormale Zuweisungen statt teure atomare acquire/release Operationen zu verwenden.



  • In der Tat, wenn man es mit extrem parallelen Architekturen zu tun hat (z.B. GPUs), hat GC potentielle Vorteile gegenüber einer klassischen manuellen Speicherverwaltung.
    Wobei ich nicht glaube, dass man dort unbedingt vollwertige Garbage Collection haben will. Der Knackpunkt ist das explizite delete. In einer extrem parallelen Umgebung ist ein explizites delete tatsächlich sehr kompliziert bzw. problematisch zu supporten.
    Imo funktioniert aber schon das Konzept von malloc()/free() bzw. eben new/delete an sich dort einfach nicht wirklich gut. Der Grund dafür liegt in der Natur der entsprechenden Hardware und Anwendungen. Ich denk jedenfalls, dass dort überhaupt grundlegend neue Konzepte gefragt sind, GC ist einfach von den bekannten Konzepten jener Fisch, der etwas schneller über das neue Land zappeln kann.
    In jedem Fall sind das Spezialanwendungen und ich finde eine weitaus sinnvollere Herangehensweise, als GC gleich mal ins Fundament einer Sprache zu gießen, ist eine der Anwendung entsprechende Speziallösung in Form einer Library. Ich bin mittlerweile sogar fast der Meinung, dass auch new und delete in ihrer jetzigen Form nicht Teil einer Sprache sein sollten (eine Erkenntnis, die ich Krishty zu verdanken hab). Eine Sprache sollte rein nur eine Möglichkeit bieten, Objekte zu konstruieren und wieder zu zerstören. Speicher besorgen und wieder zurückgeben, sollte explizit Aufgabe einer Library sein...



  • dot schrieb:

    Ich bin mittlerweile sogar fast der Meinung, dass auch new und delete in ihrer jetzigen Form nicht Teil einer Sprache sein sollten (eine Erkenntnis, die ich Krishty zu verdanken hab). Eine Sprache sollte rein nur eine Möglichkeit bieten, Objekte zu konstruieren und wieder zu zerstören. Speicher besorgen und wieder zurückgeben, sollte explizit Aufgabe einer Library sein...

    Naja...
    Im Prinzip macht C++ das ja auch so. Nur dass das Interface etwas unglücklich gewählt wurde. DAS finde ich schade, aber vom Prinzip her ist es mMn. schon OK.

    Wenn man es sich wirklich antun will, dann gibt es auch noch placement new, und als Ersatz für das fehlende placement delete darf man Destruktoren direkt aufrufen, und den Speicher danach selbst freigeben.
    (Wobei es "placement delete" strenggenommen sogar gibt, wird aber nur automatisch vom Compiler aufgerufen wenn eine Exception in einem per "placement new" aufgerufenen Konstruktor fliegt.)

    Man kann sich also jederzeit seine eigenen "new"/"new []" sowie "delete"/"delete []" Funktionen (Templates) schreiben.



  • hustbaer schrieb:

    Im Prinzip macht C++ das ja auch so. Nur dass das Interface etwas unglücklich gewählt wurde.

    Ja, das Interface ist imo in der Tat sehr unglücklich gewählt...

    hustbaer schrieb:

    Man kann sich also jederzeit seine eigenen "new"/"new []" sowie "delete"/"delete []" Funktionen schreiben.

    Theoretisch ja, in der Praxis ist das aber, aufgrund des unglücklich gewählten Interface, potentiell problematisch. Ich denk da z.B. an so Dinge, wie das Sicherstellen, dass auch überall im Programm die richtigen Versionen der Funktionen bekannt sind und verwendet werden...



  • Xin schrieb:

    kellerassel schrieb:

    hab grad wieder einen gefunden 😃

    Ausgesprochen selten, wobei mich das das selbst wundert.

    Gelegentlich mit valgrind gegentesten.

    mach ich schon, aber das langweilt mich, mysql und die c++ lib die ich verwende werfen ständig fehler und dann muss man ewig scrollen 😞



  • axo, bei mysql schauts so aus...

    ==20468== Conditional jump or move depends on uninitialised value(s)
    ==20468==    at 0x4063A2E: inflateReset2 (in /usr/lib/libz.so.1.2.3.4)
    ==20468==    by 0x4063B0C: inflateInit2_ (in /usr/lib/libz.so.1.2.3.4)
    ==20468==    by 0x4063B82: inflateInit_ (in /usr/lib/libz.so.1.2.3.4)
    ==20468==    by 0x405E34E: uncompress (in /usr/lib/libz.so.1.2.3.4)
    ==20468==    by 0x4333474: my_uncompress (in /usr/lib/libmysqlclient.so.16.0.0)
    ==20468==    by 0x435E88E: my_net_read (in /usr/lib/libmysqlclient.so.16.0.0)
    ==20468==    by 0x4358323: cli_safe_read (in /usr/lib/libmysqlclient.so.16.0.0)
    ==20468==    by 0x4358AC4: ??? (in /usr/lib/libmysqlclient.so.16.0.0)
    ==20468==    by 0x43240BB: mysql_next_result (in /usr/lib/libmysqlclient.so.16.0.0)
    ...
    


  • vllt. hab ich auch einfach was falsch gemacht 😕

    was solls es funktioniert... damit muss man auch mal zufrieden sein 😉



  • Das hat mich irgendwie an schlechte Werbung fuer Putzmittel oder Waschmittel erinnert. Das klingt so, als koenne man nicht Wasche waschen, wenn Persil oder Spee fehlt. Klar ist die vollautomatische Waschmaschine besser als das Scheuerbrett, eine Wahl faellt dabei nicht schwer. Ja gebt mir all die netten Sachen, aber gegen das Meer aus ... schlechter Software kann ich damit wenig ausrichten. Wo kommt all der schlechte Code her?

    Dann zur Sache mit dem Neuschreiben: Angenommen mir wird die Zeit gegeben, angenommen ich werde dafuer bezahlt. Angenommen ich habe eine neues funktionierendes Progr ... erstmal nur ein Programm. Angenommen es besteht auch die Tests. Was halte ich dann in den Haenden? Von aussen betrachtet hat es keinen Mehrwert, bis jetzt hat es nur Kosten verursacht, damit der Entwickler ... ruhiger schlafen kann? Vielleicht zahlt es sich in spaeteren Jahren aus, vielleicht auch nicht. Ungewiss!

    Bist du ein selbstständiger C Programmierer ? 🙂

    Eine Sache: Nimm mal eine Funktion und verifiziere / beweise formal dass die Funktion kein Speicherleck hat. Und dann gehe hin, programmiere diese mit RAII um und beweise das Ganze nochmal.

    RAII ist doch ein Weg robusteren Code zu schreiben, da man sich Anforderung allokierten Speicher wieder freizugeben durch einen Automatismus ersetzt wird. Dann spielt es auch keine Rolle mehr, dass Benutzer explizit Experten in Speicheranforderungen sein müssen, um Funktion XYZ zu manipulieren. Es entkoppelt ein wenig den Entwickler von der Funktion, in dem die Funktion einfacher für's weiterzuentwickeln macht. Gemäß dem Motto: "Funktionen sollen einfach zu benutzen sein, aber schwer zu missbrauchen sein."

    Im Embedded Bereich stößt man hier auf einen Trade-Off: Opferte man gerne ein paar Takte um sicherzustellen dass der Speicher immer intakt/konsistent bleibt ?



  • wenn ich mal meine firma mit angestellten hab, und dann einer mit: 'verwenden wir doch c++ und schreiben alles um' kommt, schmeiß ich ihn zum nächst möglichen zeitpunkt raus 😉



  • btw. ich wollte es anfangs auch in c++ machen und ich bin so froh, das ich es nicht gemacht hab 😋



  • @Bitte ein Bit: Ja ich verwende RAII, ja ich kenne die Vorzuege. Keine Ahnung, was du mir sagen moechtest. Ich will nur sagen: Schaut euch die reale Welt an, schaut euch reale Software an.

    Eine Sache: Nimm mal eine Funktion und verifiziere / beweise formal dass die Funktion kein Speicherleck hat. Und dann gehe hin, programmiere diese mit RAII um und beweise das Ganze nochmal

    Mit Disziplin bei der Implementierung geht beides gleich leicht. Das koennen sich glaube die wenigsten vorstellen. Und RAII hilft einem diszipliniert zu sein. Zu der C vs C++ Sache: Ich bin der Meinung, wer C++ kann, sollte bei C nicht total aufgeschmissen sein, wenn er etwas implementieren muss.



  • knivil schrieb:

    Ich will nur sagen: Schaut euch die reale Welt an, schaut euch reale Software an.

    und sowas postest du ins internet 🤡


Anmelden zum Antworten