Wann prüft ihr Speicherallokierungen?
-
Deswegen kapselt man in C++ den Code, welches ne Exception schmeisst in ne Classe oder Struct :o
-
kenner des new schrieb:
@tntnet
tja, da gibt es nur ein Problem, was leider nie richtig beachtet wird: Wenn man in den catch-Block kommt wurde noch kein Speicher freigegeben. Daher die Frage: Wie willst du den Text der Exception ausgeben, wenn doch kein Speicher mehr da ist?
Wenn diese Exception geworfen wurde, kannst Du nicht mal mehr ne Datei öffnen, da nicht mal mehr Speicher für ein Handle da ist. Also solltest Du vorher Speicher freigeben, bevor du ausgibst. Problem ist hier, da man ggf. nicht mehr in dem Scope ist, wo der Zeiger existiert um ihn freizugeben.
Ergo: Dein Code bringt nichts.
Ha ha ha! Hast du mal genauer nachgedacht? Du gehst davon aus das nur dann ein bad_alloc kommt, wenn absolut kein Speicher mehr frei ist. Aber ein Speichermangel kann auch dann auftreten, wenn z.B. noch 1 MByte frei ist. Denn wenn ich 2 MByte anfordere, nur noch 1 MByte frei ist, bekomme ich logischerweise ein bad_alloc. Aber deshalb kann ich noch locker z.B. noch eine Logdatei aufmachen oder sogar eine Fehler-Mail mit Loginfos an den Support schicken. Eben weil ich noch 1 MByte frei habe.
Ihr denkt nämlich nur daran, das ein bad_alloc kommt, wenn schon kein Speicher mehr frei ist. Das kann auch passieren, das heißt aber noch lange nicht, das ich deshalb die Fehlerbehandlung vernachlässigen kann. Weil das ebend nur ein Fall von viel sein kann. Kein Wunder das es soviel schlechte Software gibt, wenn alle so kurzsichtig sind.
-
Artchi schrieb:
Kein Wunder das es soviel schlechte Software gibt, wenn alle so kurzsichtig sind.
ha ha ha mal zurück. Du nennst das Kurzsichtig, aber gehst hier von "Auf gut Glück" aus, indem du sagst, das man ja nur riese Blöck allokiert und keine kleinen?
Klar, wenn das immer so ist, ist es möglich noch zu handel - aber es ist nicht immer so. Also muss deine Software ja bei diesem "Auf gut Glück" ja noch schlechter sein, da sie so kurzsichtig gehalten ist.
Tja, dumm gelaufen.
Achja, ich allokier dann grad mal 32 Byte für nen String und keine 2 MByte wie Du für 31 Zeichen + eine Nullterminierung.
-
Die Sache ist ja eher die. Wenn für das Allokieren eines Strings nicht einmal mehr ein paar Bytes übrig sind, wird weder der Prozess noch das OS zum Ausgeben irgendeiner Meldung im Stande sein. Wenn aber von den angeforderten 2 MB nur 1 MB zu Verfügung steht, könnte man noch eine Fehlermeldung rausbringen und das Programm "sauber" beenden.
Für mich würde ich dann folgende Schlüsse ziehen:
Prüfe die Allokierung bei besonders großen Allokierungen oder bei Allokierungen deren Größe von Benutzereingaben abhängt.
Prüfe die Allokierungen nicht, wenn es nur kleine (interne) sind.Ein weiterer Ansatz wäre z.B. ganz am Anfang des catch Blocks Speicher freizugeben der beim Start des Programmes allokiert/reserviert wurde, damit die Fehlerbehandlung korrekt arbeiten kann.
-
kenner des new schrieb:
Artchi schrieb:
Kein Wunder das es soviel schlechte Software gibt, wenn alle so kurzsichtig sind.
ha ha ha mal zurück. Du nennst das Kurzsichtig, aber gehst hier von "Auf gut Glück" aus, indem du sagst, das man ja nur riese Blöck allokiert und keine kleinen?
Klar, wenn das immer so ist, ist es möglich noch zu handel - aber es ist nicht immer so. Also muss deine Software ja bei diesem "Auf gut Glück" ja noch schlechter sein, da sie so kurzsichtig gehalten ist.
Tja, dumm gelaufen.
Achja, ich allokier dann grad mal 32 Byte für nen String und keine 2 MByte wie Du für 31 Zeichen + eine Nullterminierung.
Ich darf mich zitieren?
Artchi schrieb:
wenn schon kein Speicher mehr frei ist. Das kann auch passieren,
Lies mal mein Posting richtig.
Und ganz am Ende ist es völlig egal! Man muß Fehlerbehandlung in seine Programme einbauen. Jeder der das wissentlich nicht macht (was du hier forderst!) ist vorsätzlich fahrlässig!
-
Sehr lesenswert (besonders für die überzeugten "wenn kein speicher mehr da ist fliegt bad_alloc, also kann man das problem leicht behandeln"-leute): http://www.gotw.ca/publications/mill16.htm
Die Praxis ist halt nicht immer so nett.

-
Der Artikel ist aus dem Jahr 2001. Heute sieht die Sache schon wieder etwas anders aus.
Auf Systemen mit 32 Bit Adressraum ist es kein Problem soviel physikalischen Speicher zu haben dass ein Prozess alleine garnie soviel Speicher anfordern kann dass es irgendwo zu paging kommt. 4GB kosten schliesslich kein Vermögen mehr (ich hab 4 GB in meinem privaten PC).
Auf so einem System wird es also nicht zu viel paging kommen, egal wieviel Speicher man anfordert. Auch das Verhalten von Linux, Speicher "herzuggeben" der u.U. garnicht committed werden kann, ist hier kein Problem, da es eigentlich nicht dazu kommen kann (solange der PC 4 GB Speicher und noch ein paar GB Swap zur Verfügung hat).Auf Systemen mit 64 Bit Adressraum sieht es wieder eher so aus wie in dem Artikel beschrieben.
Davon abgesehen: man *kann* Programme so schreiben dass sie an jeder Stelle mit "new failure" klarkommen, es erfordert nur etwas an Aufwand. Wie in dem Artikel beschrieben ist es möglich (wenn auch oft nicht einfach) sämtliche Resourcen die benötigt werden um Fehler zu behandeln zu Beginn Anzufordern, so dass sie jederzeit zur Verfügung stehen wenn man sie braucht. Ob das für ein Programm sinn macht oder nicht kommt wohl sehr darauf an was für ein Programm man schreibt.
Für irgendwelche Programme mit denen ein User direkt arbeitet ist das wohl nur in einigen wenigen Fällen angebracht, z.B. wenn der User ein Dokument laden will das dann nichtmehr in den Speicher passt, oder er eine Operation anwirft die viel Speicher braucht (Spline-Patches in ein Mesh umwandeln kann sehr viel Speicher brauchen - nur um ein Beispiel zu nennen). Schliesslich wäre ich als User ziemlich sauer wenn ich mit einem Programm arbeite und mir das irgendwann einfach abbricht ohne mir eine Chance zu geben meine bisherige Arbeit zu sichern.
Für Programme mit denen kein User "direkt" arbeitet (z.B. Server Programme) finde ich sollte man allerdings etwas andere Standards anwenden. Z.B. ein Datenbank Server der nicht darauf achtet wieviel Speicher er anfordert, bzw. der den Fall "kein Speicher mehr da" nicht gut behandelt ist IMO unbrauchbarer Mist.
Natürlich spielt zumindest ein Punkt den Herb Sutter in dem Artikel erwähnt hier auch eine Rolle, nämlich dass es einen genauso umbringen kann wenn das OS anfängt wild zu pagen weil ein Programm zuviel Speicher verwendet. Ein Programm das mit 1/100 (oder auch nur 1/10) seiner "Original-Gescchwindigkeit" läuft ist effektiv "tot". Bei Programmen wie Datenbank Servern (sorry, mir fällt kein anderes gutes Beispiel ein), die eigentlich nie genug Speicher haben können und auch 16 oder 32 GB schnell "voll bekommen", muss man eben den max. Speicherverbrauch des Programmes anders begrenzen. Z.B. indem man den User/Admin/... einstellen lässt wieviel Speicher das Programm maximal verwenden darf (MS SQL Server macht das z.B. so). Das führt aber nicht dazu dass man sich um "bad_alloc" (bzw. allgemein den Fall "kein Speicher mehr da") nichtmehr kümmern muss, sondern führt nur zusätzlich den Fall "keine Speicheranforderung mehr erlaubt" ein.
----
Eine andere Sache die ich noch erwähnen möchte: Selbst wenn man ein Programm schreibt wo es akzeptabel ist wenn ein "bad_alloc" erst in main() (oder garnicht) gefangen wird sollte man beachten dass "bad_alloc" geworfen werden kann, und die entsprechenden Stellen "exception safe" programmieren. Tut man das nicht, könnte es passieren dass beim stack unwinding Dinge passieren die man so garnicht haben möchte.
-
Also Antworten wie "heutzutage braucht man sowas nicht mehr" sind ja wohl völlig daneben. Erstens gibts genug Embedded Anwendungen, die keine GB an Speicher bekommen und vielleicht nichtmal swappen können. Zweitens gibts durchaus Applikationen auf X86-Hardware, die ne Menge Speicher am Stück brauchen. Bei uns gibts z.B. ne CT-Software, die 1.5 GB Speicher in 18s vollknallt. Schön blöd wenn man da nen Absturz verursacht nur weil keine 1.5 GB allokiert werden konnten. Und was Fehlermeldungen anbelang, da kann man ja am Anfang des Programms eine Stück Speicher allokieren und im catch dann verwenden umd entsprechende Meldungen an den User auszugeben und Ressourcen zu sichern.
-
yogle schrieb:
... Exceptions ...immer direkt abfangt.
1.) "Pauschalisierungen sind IMMER falsch !",
sagte meine Deutschlehrerin
Hier konkret: Es ist ja gerade der große Vorteil von Exceptions gegenüber return codes, dass man sie nicht "immer direkt" behandelt, sondern nur da behandelt (und soweit fliegen lässt), wo man auch sinnvoll darauf reagieren kann.2.) Kapselung ist Dein Freund
Du musst Dich nur mit der Schnittstelle des genutzten Klassen/Funktionen (und dazu zählt eben auch das Verhalten in einem Fehlerfall) beschäftigen.
Wenn Du string/vector nutzt, brauchst Du nicht zu wissen, ob/wann die intern Speicher anfordern - sie werden Dir schon mitteilen, wann immer ihnen etwas nicht schmeckt. Vielleicht "biegen" sie das ja auch selbst wieder hin und Du merkst gar nichts davon.... auf jeden Fall sollten Klassen/Funktionen so gestrickt sein, dass man nicht hinter ihre Kulissen zu schauen braucht (und string/vector/new/... sind so).3.) Eine Ausnahme ist eine Ausnahme
Exceptionhandling ist eine seeeehr komplexe Angelegenheit, weil man oft kaum feststellen kann, was noch "funktioniert" und was nicht. Wie oben schon gesagt: Wenn kein Speicher mehr für die Anwendung zur Verfügung steht, kann es sein, dass für ausgiebige "Klimmzüge" gar kein Speicher mehr da ist.
Andererseits ist "Speicher alle" bei modernen Betriebssystemen ein sehr schwammiges Konzept. Klar, wenn ich einen fetten Progerammierfehler habe (und deswegen 17 PetaByte allokieren will), wird das noch ziemlich "gemeldet". Aber in Zeiten von "virtuellen Speicher", "swapping", .... gibt's eigentlich kaum noch bad_allocs. Da geht Dir schon viel früher schon die Performance derart in den Keller, dass das dann zum eigentlichen Problem wird.
Wenn Du natürlich auf einer speziellen Plattorm (Micro-Controller etc.) arbeitest, die sehr viel simplere Speichermechanismen und geringen Platz hat, kann das anders aussehen. Sowas schreit aber auch schnell nach einer speziellen Speicherverwaltung...
Auch ich verweise hier nochmal auf Sutters Artikel: http://www.gotw.ca/publications/mill16.htm"There's Often Little Point in Checking for New Failure Anyway"
...
1. Checking new failure is useless on systems that don't commit memory until the memory is used.
...
2. In the real world, new failure is a rare beast, made nearly extinct by the thrashing beast.
...
3. There's not always much you can do when you detect new failure.Gruß,
Simon2.
-
Aber in Zeiten von "virtuellen Speicher", "swapping", .... gibt's eigentlich kaum noch bad_allocs
Auf 32 Bit Systemen hast du dein bad_alloc schneller als du denkst. Ganz ohne Swapping etc.
Glaubs mir einfach.
-
hustbaer schrieb:
Aber in Zeiten von "virtuellen Speicher", "swapping", .... gibt's eigentlich kaum noch bad_allocs
Auf 32 Bit Systemen hast du dein bad_alloc schneller als du denkst. Ganz ohne Swapping etc.
Glaubs mir einfach.beweise? glauben kannst du in der kirche, hier zählen nur fakten.
-
Das kannst du direkt ausprobieren. Allokiere einfach solange 100MB Große Blöcke unter beispielsweise WinXP bis es kracht.
Aber das funktioniert halt nicht auf allen Plattformen. Neuere Betriebssysteme mit 64bittigem Adressraum haben halt deutlich mehr virtuellen als physischen Speicher...
-
Hi,
unabhängig, ob etwas guter Stil ist, und ob man so was macht...
Die meisten Programme, die wir hier so schreiben haben irgend was mit der Arbeit anderer Leute (oder unserer eigenen) zu tun. Da nützt ein Abpfiff ohne jeden Kommentar herzlich wenig. Wenn ich noch irgendwie gesagt bekomme, daß der Speicher alle ist, dann kann ich als Nutzer was tun, merh Swap, andere Prozesse beenden, anderen Rechner nehmen oder einfach noch ein paar Maikäfer im Rechner nachrüsten. Wenn ich es aber nicht erfahre, dann weiß ich nur es geht nicht, obwohl es vielleicht mit ein paaar "Bitchen" mehr gehen würde. Also ist das Programm für mich erst mal gestorben.
Sicher gibt es Programe, wo ein Absturz keinen Schaden tut, aber das ist nicht die Regel. Meist hat man entweder schon eine Menge eingegeben, oder es sind Dateien offen oder...
Das wichtigste wofür ich als Programmierer Sorge tragen muß, ist daß mein Programm keinen Schaden anrichtet. Also zumindest offene Dateien schließen. Wenn machbar, dann auch eventuell eingegebene Daten in eine temporäre Datei speichern, oder unvollständig geäderte Daten verwerfen statt sie unkonsistent zu speichern.
Das hängt immer davon ab, was die Daten an denen das Programm arbeitet wert sind. Wenn es nur eine Stunde Arbeit ist, ist es die eine Sache, aber man muß IMMER davon ausgehen, daß es noch Kunden gibt, die keine Datensicherung kennen. Wenn dann die gesammten Geschäftsgrundlagen eines kleinen Betriebes den Bach runter sind, dann hilft der Hinweis auf Datensicherungen wenig. Da können im schlimmsten Fall Arbeitsplätze unbeteiligter dritter dranhängen.
Also auf den Punkt gebracht zumindest immer versuchen über Ressourcenknappheit zu informieren, und je nach Wert der zu bearbeitenden Daten gegebenenfalls einen Notfallplan abarbeiten, der das schlimmste verhindert.
Für eine Meldung sollte der Speicher fast immer reichen, da durch das bei einer Exception stattfindende stack unwinding ja auch wieder speicher frei wird. Aber das ist ja auch nicht das Problem. Wo ist die Schwierigkeit mal ein kleies Programm zu schreiben, dem man sagen kann wieviel Platz es belegen soll und das dann bei abgeschaltetem swap parallel starten, den Platz allemachen und gucken was passiert.Wesentlich weniger schlimm finde ich die Verluste durch nicht wieder freigegebenen Speicher, wo z.B. Myers so viel drüber schreibt. Solange man nicht regelmäßig was vergißt tut das bei den heutigen Systemen aus meiner sicht fast gar nichts. Wenn irgend eine Seite nicht mehr benutzt wird, wird sie irgendwann sowieso vom System ausgelagert und liegt dann bis zum nächsten Neustart im swap. Wenns die Regel wird, oder bei sehr großen Speicherblöcken passiert ists aber auch da nicht schön. Aber auf keinen Fall en Argument gegen C++ wie es manche anführen, weil es da keine automatische Speicherbereinigung gibt. Aber mit sorgfältiger Programiereung und eventuell mal durch die Lappüen rutschenden Blocks denke ich kann man leben.
Gruß Steffen das Mümmel
-
Eben hatte mein Kollege eine Exception wegen Speicherüberlauf. Das Programm hat den Fehler gemeldet und ist weiter gelaufen. Die Fehlermeldung hat ihm gleich gesagt, daß zu viel Speicher verbraucht wurde. In dem Fall hat er Daten in ein std::ostringstream geschrieben. Und zwar offensichtlich ein wenig zu viel. Der ostringstream hat einen std::bad_alloc geworfen. Der Fehler wurde abgefangen und da der ostringstream auf dem Stack lag, hat er vor dem catch den bisher verbrauchten Speicher frei gegeben, so daß die Gefahrensituation nicht mehr gegeben war und das Programm problemlos weiter arbeiten konnte.
Ist hier noch irgendjemand, der bezweifelt, daß ein Speicherüberlauf ignoriert werden sollte, da sowieso alles zu spät ist?
Gruß
Tntnet
-
Das zweifeln wahrscheinlich nur die an, die keine kritische Software entwickeln. Und mit kritisch meine ich ganz bestimmt nicht nur Software in einem AKW! Sondern kritisch ist bei uns schon eine Planungsstoftware für die Planer eines Produktes. Aber auch ein Exceldokument ist kritische Software: wenn ich was die ganze Zeit eingebe und berechne, und aufeinmal ist alles weg, stehe ich ganz schön dumm da, wenn der Chef zum Feierabend ein Ergebnis sehen will. Völlig unerheblich ob ich vielleicht alle paar Sekunden Strg+S hätte drücken sollen.
Übrigens, auch abstürzende PC-Spiele ohne nennenswerte Angabe eines Grundes sind nervig. Und die sind nur zur Belustigung nützlich. Wenn schon der Absturz eines PC-Spiels nervt, kann man sich das bei "ernsthaften" Anwendungen vorstellen.
-
Artchi schrieb:
Übrigens, auch abstürzende PC-Spiele ohne nennenswerte Angabe eines Grundes sind nervig. Und die sind nur zur Belustigung nützlich. Wenn schon der Absturz eines PC-Spiels nervt, kann man sich das bei "ernsthaften" Anwendungen vorstellen.
Das verwundert mich auch immer. PC-Spiele an denen ein Haufen Leute sitzen und eine Menge Geld reingesteckt wird, stürzen mit Ach und Krach ab. Wieso kann man da nicht einen einfachen Exception Handler reinbauen der z.B. die Spielstände speichert und den nächsten Patch könnten sie vielleicht auch noch schneller rausbringen

-
Weil das eben nicht so einfach ist, Freund yogle.
z.B. kannst du schonmal davon ausgehen dass bei vielen dieser Fehler der Spielstand im RAM sowieso schon "kaputt" ist, den zu speichern bringt dann auch nixmehr.
Weiters, wenn der Fehler irgendwo mitten im Gamestate Update passiert hast du einen "Zwischenzustand" den du auch nicht sinnvoll speichern kannst.
-
yogle schrieb:
PC-Spiele an denen ein Haufen Leute sitzen und eine Menge Geld reingesteckt wird, stürzen mit Ach und Krach ab. Wieso kann man da nicht einen einfachen Exception Handler reinbauen der z.B. die Spielstände speichert...
kann man schon, aber es interessiert wohl keinen. ich glaube, auf irgendwelche fehlschläge wird bei spiele-codes kaum rücksicht genommen, solange der kram in den meisten fällen läuft.

-
Das wäre kontraproduktiv autamatische Spielstandsicherung einzubauen! Schließlich verlängert sich dadurch die Spieldauer

Wenn man jedes mal von vorn anfangen muss....
-
hustbaer schrieb:
Weil das eben nicht so einfach ist, Freund yogle.
z.B. kannst du schonmal davon ausgehen dass bei vielen dieser Fehler der Spielstand im RAM sowieso schon "kaputt" ist, den zu speichern bringt dann auch nixmehr.
Weiters, wenn der Fehler irgendwo mitten im Gamestate Update passiert hast du einen "Zwischenzustand" den du auch nicht sinnvoll speichern kannst.
Ich weiß schon recht genau, was grundsätzlich passiert wenn ein Programm abstürtzt, das kannste mir glauben. Das man größtenteils davon ausgehen kann, dass der Spielstand im RAM eh schon "kaputt" ist, denke ich nicht. Eher umgekehrt. Denn der wohl häufigste Fehler der zu Abstürzen führt, ist die Access Violation. Da verändert sich im RAM manchmal gar nichts, manchmal schreibt/ließt man an der Stelle 0x0 im Speicher (da steht praktisch nie was), oder man verwendet uninitialisierte Pointer die dann so ausehen 0xABABABAB (geringe Chance hier was zu treffen). Natürlich gibt es noch zig andere Fehlerquellen aber naja bla... Ich will jetzt hier auch nicht über die (Un)Möglichkeit Spielstände zu speichern streiten.
Was ich wichtiger finde, ist der fehlende Feedback. Wenn das Programm abschmiert, sendet es garantiert keinen Bugreport und diese können sehr nützlich sein. Nicht umsonst hat Microsoft (endlich) die Minidumps eingebaut.