string.erase = Memory Leak?



  • Wieso nimmt man bei http-Downloads überhaupt std::string ?
    ich würde ja sagen std::vector<char> oder (vor allem, wenn man dieses Problem hat) std::deque<char> .

    bb



  • pumuckl schrieb:

    Dieses shrink_to_fit macht vermutlich genau das beschriebene intern: allokiert genügend Speicher für den kleinen String, kopiert die Daten dort hinein und gibt dann den größeren Speicherblock frei. Vermutlich garniert mit einer kleinen Überprüfung, ob size() viel kleiner als capacity(), um ggf. die Kopie zu sparen.

    Ja, das Weglassen von unnötigen Kopien ist der entscheidende Vorteil. Es gibt ja nicht nur den Fall, dass sich das Verkleinern nicht lohnt, es gibt ja auch den Fall, dass das Verkleinern gar nicht möglich ist (small string optimization).

    Artchi schrieb:

    Nö, das ist kein Memoryleak... vielleicht in manchen Scenarien etwas uneffektiv. Ein Memleak heißt, das der Speicher "verloren" ist, und man ihn nicht mehr kontrollieren (z.B. freigeben) kann... z.B. weil man die Speicherstelle nicht mehr kennt.

    Du machst es daran fest, dass man den Speicher überhaupt nicht mehr kontrollieren kann - ich daran, dass das Programm den Speicher nicht mehr benötigt und es versäumt wird, den Speicher (rechtzeitig) freizugeben. Wenn du danach gehst, dass der Speicher ja noch kontrolliert werden könnte, auf ihn zugegriffen werden könnte, dann hätten Javaprogramme keine Speicherlecks - freigegeben wird der Speicher garantiert immer und es gibt immer noch (mindestens) einen Verweis, über den man auf das Objekt zugreifen könnte. Nur weil Speicher potenziell noch genutzt werden könnte, heißt das noch lange nicht, dass es kein Speicherleck ist.
    Da man dies aber nicht aus den Aussagen des Threadstarters herauslesen konnte, habe ich auch geschrieben, dass für eine Einschätzung dazu Informationen fehlen.

    Wie würdest du denn sowas bezeichnen?

    void init_strings(std::vector<std::string> &my_strings)
    {
         my_strings.resize(500);
         for (std::vector<std::string>::iterator it = my_strings.begin(), end = my_strings.end(); it != end; ++it)
         {
              // heavy operation - needs memory
              it->reserve(1000000);
              // do some work...
              // assign result:
              it->assign("Hello!");
         }
    }
    
    int main()
    {
       std::vector<std::string> many_strings;
       init_strings(many_strings);
       // run the main part of the program
       program();
       // done :-)
    }
    

    Das Programm braucht für die ganze Lebenszeit ca. 500 MB Speicher - würdest du behaupten, es hat kein Speicherleck? Was dann?
    Natürlich wird man so etwas in der Realität nur selten finden - normalerweise muss man diese Handoptimierungen nicht machen.

    unskilled schrieb:

    Wieso nimmt man bei http-Downloads überhaupt std::string ?
    ich würde ja sagen std::vector<char> oder (vor allem, wenn man dieses Problem hat) std::deque<char>.

    Was wäre der große Vorteil von std::vector<char> gegenüber std::string ? Wenn man den HTTP-Download nachher eh als Zeichenkette behandeln will, warum dann nicht auch gleich als solche einlesen? Vor allem, weil die aktuellen Working Drafts nahezu die gleichen Garantien bei std::vector<T> und std::basic_string<T> geben (vielleicht sogar inzwischen die klitzegleichen, doch dazu müsste ich mich mehr in den Text des Standards reinlesen). Eigentlich ist std::basic_string<T> garantiert nur ein auf Zeichenketten optimierter, monolithischer Vektor. Warum also den Umweg gehen?

    std::deque<T> könnte hier tatsächlich eine bessere Wahl zu sein. Da gibt's nur eines: messen.



  • Old McDonald schrieb:

    Artchi schrieb:

    Nö, das ist kein Memoryleak... vielleicht in manchen Scenarien etwas uneffektiv. Ein Memleak heißt, das der Speicher "verloren" ist, und man ihn nicht mehr kontrollieren (z.B. freigeben) kann... z.B. weil man die Speicherstelle nicht mehr kennt.

    Du machst es daran fest, dass man den Speicher überhaupt nicht mehr kontrollieren kann - ich daran, dass das Programm den Speicher nicht mehr benötigt und es versäumt wird, den Speicher (rechtzeitig) freizugeben. Wenn du danach gehst, dass der Speicher ja noch kontrolliert werden könnte, auf ihn zugegriffen werden könnte, dann hätten Javaprogramme keine Speicherlecks - freigegeben wird der Speicher garantiert immer und es gibt immer noch (mindestens) einen Verweis, über den man auf das Objekt zugreifen könnte. Nur weil Speicher potenziell noch genutzt werden könnte, heißt das noch lange nicht, dass es kein Speicherleck ist.
    Da man dies aber nicht aus den Aussagen des Threadstarters herauslesen konnte, habe ich auch geschrieben, dass für eine Einschätzung dazu Informationen fehlen.

    Wie würdest du denn sowas bezeichnen?

    void init_strings(std::vector<std::string> &my_strings)
    {
         my_strings.resize(500);
         for (std::vector<std::string>::iterator it = my_strings.begin(), end = my_strings.end(); it != end; ++it)
         {
              // heavy operation - needs memory
              it->reserve(1000000);
              // do some work...
              // assign result:
              it->assign("Hello!");
         }
    }
    
    int main()
    {
       std::vector<std::string> many_strings;
       init_strings(many_strings);
       // run the main part of the program
       program();
       // done :-)
    }
    

    Das Programm braucht für die ganze Lebenszeit ca. 500 MB Speicher - würdest du behaupten, es hat kein Speicherleck? Was dann?

    Das ist kein Speicherleck, sondern Speicherverschwendung. Ich meine, der Begriff "Memory Leak" ist einfach so definiert, dass es keine Möglichkeit gibt, auf den Speicher zuzugreifen - und ihn irgendwann freizugeben. S. z.B. hier: http://de.wikipedia.org/wiki/Speicherleck. Was bringt's dir, plötzlich eine andere Definition auszupacken? Das führt doch nur dazu, dass man aneinander vorbei redet.

    Java-Programme geben ihre "Pointer" einfach auf und vertrauen auf den Garbage Collector, dass er sich um den "leckenden" Speicher kümmert. Auch hier finde ich es wenig zielführend (und auch ein bischen polemisch), von Memory Leaks zu sprechen.

    Stefan.



  • http://en.wikipedia.org/wiki/Memory_leak gibt genau meine Definition wieder. Das Programm versäumt es, Speicher freizugeben, den es nicht mehr benötigt. Im Gegensatz zum häufigsten Fall aber hätte es potenziell noch die Möglichkeit, den Speicher zu nutzen (wird es nur nicht tun). Du reduziert das auf den Spezialfall, dass der Speicher nicht mehr angesprochen werden kann (der aber in C++ die wohl häufigste Form eines Speicherlecks ist).

    Es ist durchaus üblich, auch in Java in diesem Fall von Memory Leak zu sprechen. Das wäre so etwas , wie wenn noch irgendwo ein std::tr1::shared_ptr<T> irgendwo im Programm die Speicherfreigabe verhindert. Wenn das passiert, wie nennst du das dann? Wohl kaum Speicherverschwendung, oder?

    Im Übrigen gibt es ja auch noch Abschnitte zu den verschiedenen Techniken, um Speicherlecks zu umgehen (auch in der deutschen Wikipedia) - und auch dort wird erwähnt, dass es in Sprachen mit GC durchaus zu Speicherlecks kommen kann.

    Natürlich sind diese Probleme meist bei std::vector<T> und std::basic_string<T> nicht relevant, das heißt aber nicht, dass diese nicht größere Speicherlecks produzieren können.



  • Old McDonald! Deine Definition wird nicht richtig, nur weil du der Meinung bist, das es so ist. Wenn ich sage, die Erde ist eine Scheibe, weil ich diser Meinung bin, wird es deshalb nicht richtig.



  • Old McDonald! Deine Definition wird nicht richtig, nur weil du der Meinung bist, das es so ist. Wenn ich sage, die Erde ist eine Scheibe, weil ich diser Meinung bin, wird es deshalb nicht richtig.

    Was ist denn das für ein Argument? 😕
    Wir diskutieren hier über Definitionen, nicht über Behauptungen. Außerdem beinhaltet die Definition, die ich bevorzuge, die eure und verallgemeinert diese. Von dem her habe ich nicht das Gegenteil von eurer bevorzugten Definition behauptet. Der Vergleich mit der Erde und der Scheibe hinkt deswegen.

    Es gibt nun mal nicht die eine Definition für Speicherleck.
    Eure Definition:
    Wenn Speicher nicht mehr vom Programm benötigt wird und nicht mehr für das Programm erreichbar ist, und es versäumt wurde, den Speicher freizugeben, so gibt es ein Speicherleck.
    Meine Definition:
    Wenn Speicher nicht mehr vom Programm benötigt wird, und es versäumt wurde, den Speicher (rechtzeitig) freizugeben, so gibt es ein Speicherleck.

    Die erste Definition ist stärker im Bereich der Sprache C++ verbreitet, da in ihr Speicher deterministisch und explizit freigegeben wird. Doch vor allem in Sprachen mit GC musste man feststellen, dass es eben noch eine andere Art von Speicherleck gibt, denn gerade dort kann das Problem entstehen, dass Objekte referenziert werden, die nicht mehr benötigt werden (in C++ hätte man dann meist einen dangling pointer - auch ein mögliches Problem, aber wenn eh nicht mehr auf das Objekt zugegriffen wird...). Und an dem Punkt nützt auch ein GC nichts mehr, denn das Objekt könnte ja potenziell noch genutzt werden. Es ist eben nicht nur ein Problem, wenn Speicher nicht freigegeben wird, es kann auch ein Problem sein, dass Speicher zu spät (oft dann am Programmende) freigegeben wird. Auch dann kann man beobachten, wie der Speicherverbrauch immer weiter zunimmt, bis das Programm crasht.
    Mir kommt es nicht darauf an, wieso das Programm zu viel Speicher verbraucht, wieso es mehr und mehr Speicher benötigt und ob es möglicherweise den Speicher noch selbst freigeben kann (und vielleicht auch am Sankt Nimmerleinstag mal machen wird), mir geht es um die Auswirkungen, die Folgen - und die sind in beiden Fällen gleich.

    Warum soll es nicht eine Verallgemeinerung der engen Definition geben? Sie wird ja im deutschen Wikipediaartikel angedeutet und im englischen Wikipediaartikel ist diese Einschränkung, dass auf Speicher nicht mehr zugegriffen werden kann, nicht in der Definition vorhanden. Auch bei Wikipedia lassen sich Diskussionen darüber finden, was ein Speicherleck ist, und es gibt auch dort beide Sichtweisen.

    Ich muss aber zugeben, mein Beispiel war schlecht. Doch es ist ziemlich schwer, ein realistisches Beispiel für diesen etwas subtileren Fall eines Speicherlecks zu finden, vor allem, wenn dann auch noch std::string s den unnötigen Speicher belegen sollen und sich dieses Problem auch noch deutlich mit der Zeit manifestieren soll. Hinzu kommt noch, dass C++ ja keinen GC hat, wo dieses Problem ja am häufigsten auftritt. Eine einfache Endlosschleife, wo ständig ein String erstellt wird, der unnötig viel Speicher belegt, wäre wohl kaum ein Argument. Doch es gibt dieses Problem, und bei IBM sind zu Java-Speicherlecks schöne Artikel zu finden:
    http://www.ibm.com/developerworks/library/j-leaks/index.html
    http://www.ibm.com/developerworks/rational/library/05/0816_GuptaPalanki/

    Normalerweise muss man sich über diesen etwas subtileren Typ eines Speicherlecks in C++ keine Gedanken machen; ohne GC wird es deutlich komplizierter, so ein Speicherleck zu schaffen (am ehesten wohl noch mit std::tr1::shared_ptr<T> ).

    Wir werden wohl an dieser Stelle keine Einigkeit erzielen können. Das war aber auch nicht mein Ziel. Es ging mir jetzt eher darum, zu zeigen, dass es noch einen anderen Standpunkt gibt.


  • Mod

    Es gibt nun mal nicht die eine Definition für Apfel.
    Eure Definition:
    Die Frucht eines bestimmten Kernobstgewächses aus der Familie der Rosengewächse.
    Meine Definition:
    Alle süßen Feldfrüchte.

    Die erste Definition ist stärker im Bereich der Gärtnerei verbreitet, da in ihr Früchte explizit bezeichnet werden müssen. Doch in allen anderen Bereichen muss man feststellen, dass es noch andere Arten von Obst gibt, die sich von dem was ihr als Äpfel bezeichnet kaum unterscheiden.
    Mir kommt es nicht darauf an, was ich genau esse, sondern mir geht es um die Auswirkungen, die Folgen - und die sind in beiden Fällen gleich.

    Warum soll es nicht eine Verallgemeinerung der engen Definition geben? Sie wird ja im deutschen Wikipediaartikel angedeutet und im englischen Wikipediaartikel ist diese Einschränkung, dass Äpfel unbedingt nur klassische Äpfel bezeichnet nicht in der Definition vorhanden. Auch bei Wikipedia lassen sich Diskussionen darüber finden, was ein Apfel ist, und es gibt auch dort beide Sichtweisen.

    Wir werden wohl an dieser Stelle keine Einigkeit erzielen können. Das war aber auch nicht mein Ziel. Es ging mir jetzt eher darum, zu zeigen, dass es noch einen anderen Standpunkt gibt.



  • Old McDonald schrieb:

    ....Du machst es daran fest, dass man den Speicher überhaupt nicht mehr kontrollieren kann - ich daran, dass das Programm den Speicher nicht mehr benötigt und es versäumt wird, den Speicher (rechtzeitig) freizugeben. ...

    Nach der Definition haben wir hier aber auch ein Speicherleck, oder?

    int f();
    
    int main() {
       int i = 3;
       cout << i;
    
       // Oooooh!! Speicherleck! i wird weder gebraucht noch freigegeben!
       return f();
    }
    

    Gruß,

    Simon2.



  • Ich halte nach wie vor die Definition für richtig, die als Bedingung enthält, dass das Programm keinen Zugriff mehr auf den Speicher hat. Ohne dies, scheint mir, gibt es überall Speicherlöcher (oder wenigstens den Verdacht auf Speicherlöcher) und der Begriff verwässert. Und die unsachgemäße Verwendung von shared_ptr und Konsorten, bei denen vielleicht Objekte ringförmig aufeinander verweisen, ohne dass das Programm selbst noch Zugriff auf diesen Ring hätte, passt durchaus in diese Definition.

    Mit Garbage Collectors habe ich mich schon lange nicht mehr beschäftigt. Damals im Zusammenhang mit GCs für C++. Wenn ich mich recht entsinne, kann es, je nach Implementierung des GC, durchaus dazu kommen, dass Speicher vom Collector nicht freigegeben wird, obwohl er nicht mehr referenziert wird. Das wäre dann ebenfalls ein Speicherloch.

    Doch kann man sich ja durchaus fragen, ob ein GC tatsächlich Teil des Programms ist - zumindest in dem Zusammenhang, in dem wir diskutieren. Ich würde eher dazu neigen, ihn der Laufzeitumgebung zuzuschlagen. Dann aber macht es keinen Sinn, von Speicherlöchern zu reden. Es ist schließlich (erneut: per Definition) nicht mehr Aufgabe des Programms, Speicher frei zu geben.

    Dies gilt insbesondere für Java, wo der GC Teil der VM ist und wo man meines Wissens gar keinen Zugriff auf den GC hat und dessen Arbeitsweise nicht beeinflussen kann.

    Stefan.



  • Doch bei Java kann man wohl durchaus das Löschen der Objekte manuell auslösen, weil der das sonst nur macht, wenn Rechenzeit übrig ist. Wenn das Ding aber unter Volllast läuft, würde der GC sonst nie dran kommen, daher sollte man das Ding auch manuell mal aufrufen können.

    Ansonsten unterschreibe ich hier Stefans Ansicht. Wenn wir nach OldMcDonalds Definition gehen, dann wäre ständig Code voller Speicherlecks. Nach Stefans Definition, haben wir dann nur sehr selten den Fall eines Speicherlecks 😉



  • It0101 schrieb:

    Doch bei Java kann man wohl durchaus das Löschen der Objekte manuell auslösen, weil der das sonst nur macht, wenn Rechenzeit übrig ist. Wenn das Ding aber unter Volllast läuft, würde der GC sonst nie dran kommen, daher sollte man das Ding auch manuell mal aufrufen können.

    Der GC läuft in Java in einem separaten Thread, der auf jeden Fall mal drankommt, wenn auch nciht mit höchster Prio. Du kannst die Maschine also noch so knechten, der GC kommt trotzdem zum Zuge. Nichtsdesotrotz gibts natürlich trotzdem Möglichnkeiten, den GC und die verwendeten Strategien zu tunen.



  • ja das meinte ich auch. Wie das nun genau ist, weiß ich auch nicht, weil ich bisher nur nativ programmiert habe 😉 Java is ne Insel und mehr nich 😉



  • Warum habe ich mich nur auf eine Diskussion über Probleme in GC-Sprachen in einem C++-Forum eingelassen?

    Ein Speicherleck muss sich natürlich auch manifestieren. Es ist natürlich Blödsinn, an jeder Ecke ein solches zu sehen, doch wenn es sich manifestiert, dann sieht es aus wie ein Speicherleck, verhält sich wie ein Speicherleck und wirkt wie ein Speicherleck, also ist es ein Speicherleck. (Ums auf die Botanik zu übertragen: Wenn's aussieht wie ein Apfel, riecht wie ein Apfel, schmeckt wie ein Apfel und dieselbe Wirkung hat wie ein Apfel, dann ist es ein Apfel). Ob das Programm noch darauf Zugriff haben könnte und den Speicher irgendwann mal freigibt, ist mir dann schnuppe, mir geht es darum, dass es eben nichts mehr mit dem Speicher anfängt und sich ein Speicherproblem mit der Zeit immer mehr verstärkt.

    Das Problem ist, wenn man die Bedingung noch hinzunimmt, dass das Programm den Speicher weder freigeben noch nutzen kann, dann kann man mit std::tr1::shared_ptr<T> durchaus etwas bauen, wo Speicher unnötig gehalten wird. Ich meine damit keine zyklischen Referenzen, eher ein Problem, dass über mehrere tausend Zeilen Code passiert: Ein Modul hält einen std::tr1::shared_ptr<T> auf ein Objekt, doch kann mit dem nichts mehr anfangen, weil es nur auf dieses zugreift, wenn von außen der Befehl dazu kommt (wie auch immer). Doch im restlichen Programm wird angenommen, dass das Objekt nicht mehr existiert, also kommt auch kein solcher Befehl -> Speicherleck. Der std::tr1::shared_ptr<T> garantiert aber, dass das Objekt auf jeden Fall freigegeben wird.

    Ich glaube, ihr bräuchtet ein Beispiel. In C++ so etwas zu bauen, was nicht direkt offensichtlich ist, ist schwierig, vor allem, weil C++ keinen GC hat und man sich Gedanken über ownership machen muss, in Java kenne ich mich nicht gut genug aus. Wo ich aber etwas Verrücktes bauen kann und es nicht direkt offensichtlich ist, dass es ein Problem gibt, ist Lua. Ich könnte in dem Programm weiterhin auf die den Speicher belegenden Objekte zugreifen (über Iteratoren), der Speicher wird garantiert am Ende freigegeben, ich erlaube dem GC ausdrücklich, diese Objekte zu sammeln; doch der Speicherbedarf steigt und steigt, und die Ursache dafür lässt sich nicht im Lua-Manual finden (auch wenn sie durchaus woanders dokumentiert ist). Eins wäre bei diesem Beispiel sicher: Der Begriff Speicherverschwendung wäre völlig unangebracht. Doch wenn man das Nicht-mehr-kontrollieren-können hinzunimmt, dann wäre es eben auch kein Speicherleck, denn die Kontrolle über die Objekte wäre durchaus noch möglich! Doch ich glaube kaum, dass sich hier jemand dafür den Lua-ANSI-C-Interpreter holt, also wäre es wohl unnötige Mühe, das Programm zu schreiben.



  • Aber aus unserer Sicht ist

    ...Ob das Programm noch darauf Zugriff haben könnte...

    eine Eigenschaft das Apfels. Aus deiner Sicht nicht.

    Es sind halt unterschiedliche Ansichten, daher erübrigt sich die weitere Diskussion hier wohl.



  • It0101 schrieb:

    daher erübrigt sich die weitere Diskussion hier wohl.

    ja bitte 😛


Anmelden zum Antworten