Frage Ist "Nicht genügend Speicher" ein behebbarer Fehler?


Ich habe lange programmiert, und die Programme, die ich sehe, wenn sie keinen Speicher mehr haben, versuchen aufzuräumen und zu beenden, d. H. Ich kann mich nicht daran erinnern, wann ich das letzte Mal gesehen habe, wie ich mich tatsächlich erholt habe und normal weiterarbeiten kann.

So viel Verarbeitung hängt davon ab, Speicher erfolgreich zuordnen zu können, insbesondere in Garbage Collection-Sprachen. Es scheint, dass Fehler wegen zu wenig Arbeitsspeicher als nicht wiederherstellbar eingestuft werden. (Nicht behebbare Fehler sind beispielsweise Stapelüberläufe.)

Was ist das zwingende Argument dafür, einen behebbaren Fehler zu machen?


75
2017-12-02 11:55


Ursprung


Antworten:


Es hängt wirklich davon ab, was Sie bauen.

Es ist nicht völlig unvernünftig, dass ein Webserver ein Request / Response-Paar durchlässt, aber dann weitere Anfragen annimmt. Sie müssten sicher sein, dass der einzelne Fehler keine negativen Auswirkungen auf den globalen Staat hatte - das wäre das heikel. Da ein Fehler in den meisten verwalteten Umgebungen (z. B. .NET und Java) eine Ausnahme verursacht, vermute ich, dass die Behandlung der Ausnahme in "Benutzercode" für zukünftige Anforderungen - z. Wenn eine Anfrage versucht, 10 GB Speicher zuzuordnen und fehlzuschlagen, sollte das den Rest des Systems nicht beschädigen. Wenn das System nicht genügend Arbeitsspeicher zur Verfügung hat, während es versucht, die Anfrage an den Benutzercode zu übergeben, könnte dies jedoch unangenehmer sein.


35
2017-12-02 12:04



In einer Bibliothek möchten Sie eine Datei effizient kopieren. Wenn Sie das tun, werden Sie normalerweise feststellen, dass das Kopieren mit einer kleinen Anzahl von großen Blöcken viel effektiver ist als das Kopieren vieler kleinerer (z. B. ist das Kopieren einer 15-MB-Datei durch Kopieren von 15 1MB-Blöcken schneller als Kopieren von 15'000) 1K Brocken).

Aber der Code funktioniert mit jeder Chunk-Größe. Obwohl es bei 1MB-Chunks möglicherweise schneller ist, sollten Sie OutOfMemoryError abfangen und die Chunk-Größe reduzieren, bis Sie erfolgreich sind, wenn Sie ein System entwerfen, in dem viele Dateien kopiert werden.

Ein anderer Ort ist ein Cache für Objekte, die in einer Datenbank gespeichert sind. Sie möchten so viele Objekte wie möglich im Cache speichern, aber Sie möchten den Rest der Anwendung nicht stören. Da diese Objekte neu erstellt werden können, ist es eine clevere Möglichkeit, Speicher zu sparen, um den Cache an einen Speicherkontroller anzuhängen, um Einträge zu löschen, bis der Rest der App wieder genug Platz zum Atmen hat.

Schließlich möchten Sie zur Bildbearbeitung so viel Bild wie möglich in den Speicher laden. Auch hier können Sie mit einem OOM-Handler implementieren, ohne im Voraus zu wissen, wie viel Speicher der Benutzer oder das Betriebssystem Ihrem Code zur Verfügung stellt.

[EDIT] Beachten Sie, dass ich hier unter der Annahme arbeite, dass Sie der Anwendung eine feste Speichermenge gegeben haben und dieser Betrag kleiner ist als der gesamte verfügbare Speicher ohne Auslagerungsspeicher. Wenn Sie so viel Speicher zuweisen können, dass ein Teil davon ausgelagert werden muss, machen einige meiner Kommentare keinen Sinn mehr.


17
2017-12-02 12:16



Benutzer von MATLAB haben bei der Ausführung von Arithmetik mit großen Arrays ständig zu wenig Speicher. Wenn beispielsweise die Variable x in den Speicher passt und sie "x + 1" ausführen, ordnet MATLAB dem Ergebnis Platz zu und füllt es dann. Wenn die Zuweisung MATLAB-Fehler nicht besteht und der Benutzer etwas anderes versuchen kann. Es wäre eine Katastrophe, wenn MATLAB jedes Mal, wenn dieser Anwendungsfall auftrat, ausstieg.


9
2017-12-02 16:08



OOM sollte wiederherstellbar sein, da das Herunterfahren nicht die einzige Strategie ist, die von OOM wiederhergestellt werden kann.

Es gibt tatsächlich eine hübsche Standardlösung für das OOM-Problem auf Anwendungsebene. Bestimmen Sie im Rahmen Ihres Anwendungsdesigns eine sichere Mindestspeichermenge, die für die Wiederherstellung aus einem nicht ausreichenden Arbeitsspeicher erforderlich ist. (ZB der Speicher, der zum automatischen Speichern von Dokumenten benötigt wird, Warndialoge aufrufen, Abschaltdaten protokollieren).

Zu Beginn Ihrer Anwendung oder zu Beginn eines kritischen Blocks sollten Sie diese Speichermenge vorab reservieren. Wenn Sie einen Speichermangel feststellen, geben Sie Ihren Sicherheitsspeicher frei und führen Sie die Wiederherstellung durch. Die Strategie kann immer noch scheitern, aber im Großen und Ganzen gibt es einen großen Preis für das Geld.

Beachten Sie, dass die Anwendung nicht heruntergefahren werden muss. Es kann einen modalen Dialog anzeigen, bis die OOM-Bedingung aufgelöst wurde.

Ich bin mir nicht 100% sicher, aber ich bin mir ziemlich sicher 'Code abgeschlossen'(Pflichtlektüre für jeden seriösen Softwareentwickler) deckt dies ab.

P.S. Sie können Ihren Anwendungsrahmen erweitern, um bei dieser Strategie zu helfen, aber bitte implementieren Sie eine solche Richtlinie nicht in einer Bibliothek (gute Bibliotheken treffen keine globalen Entscheidungen ohne eine Einverständniserklärung)


7
2018-01-03 22:37



Ich denke, dass es wie viele Dinge eine Kosten-Nutzen-Analyse ist. Sie kann Programm bei der versuchten Wiederherstellung von einem malloc () - Fehler - obwohl es schwierig sein kann (Ihr Handler sollte besser nicht mit dem gleichen Speichermangel in Konflikt geraten, mit dem es umgehen soll).

Sie haben bereits bemerkt, dass der häufigste Fall ist, sauber zu machen und ordnungsgemäß zu versagen. In diesem Fall wurde entschieden, dass die Kosten für einen ordnungsgemäßen Abbruch geringer sind als die Kombination aus Entwicklungskosten und Leistungskosten bei der Wiederherstellung.

Ich bin mir sicher, dass Sie an Ihre eigenen Beispiele von Situationen denken können, in denen die Beendigung des Programms eine sehr teure Option ist (Lebenserhaltungsmaschine, Raumschiffsteuerung, langfristige und zeitkritische Finanzrechnung usw.) - obwohl die erste Verteidigungslinie ist Natürlich, um sicherzustellen, dass das Programm vorhersehbare Speicherauslastung hat und dass die Umgebung dies bereitstellen kann.


5
2017-12-02 12:08



Ich arbeite an einem System, das Speicher für IO-Cache zuweist, um Leistung zu erhöhen. Bei der Erkennung von OOM wird dann etwas davon zurückgenommen, so dass die Geschäftslogik fortfahren kann, auch wenn dies weniger IO-Cache und etwas geringere Schreibleistung bedeutet.

Ich habe auch mit eingebetteten Java-Anwendungen gearbeitet, die versucht haben, OOM zu verwalten, indem sie die Garbage-Collection erzwingen und optional einige nicht-kritische Objekte wie vorab abgerufene oder zwischengespeicherte Daten freigeben.

Die Hauptprobleme bei der OOM-Behandlung sind:

1) in der Lage sein, an der Stelle, wo es passiert ist, erneut versuchen zu können oder in der Lage zu sein, zurück zu rollen und von einem höheren Punkt aus erneut zu versuchen. Die meisten zeitgenössischen Programme verlassen sich zu sehr auf die Sprache, die sie werfen müssen, und verwalten nicht wirklich, wo sie enden und wie sie die Operation erneut versuchen. In der Regel wird der Kontext der Operation verloren gehen, wenn sie nicht dazu gedacht ist, erhalten zu bleiben

2) in der Lage zu sein, tatsächlich etwas Speicher freizugeben. Dies bedeutet eine Art Ressourcenmanager, der weiß, welche Objekte kritisch sind und welche nicht, und das System kann die freigegebenen Objekte erneut anfordern, wenn sie später kritisch werden

Ein weiteres wichtiges Problem besteht darin, dass ein Rollback durchgeführt werden kann, ohne dass eine weitere OOM-Situation ausgelöst wird. Dies ist etwas, das in höheren Sprachen schwer zu kontrollieren ist.

Außerdem muss sich das zugrunde liegende Betriebssystem in Bezug auf OOM vorhersehbar verhalten. Linux wird zum Beispiel nicht, wenn der Speicher übercommit aktiviert ist. Viele Swap-fähige Systeme werden früher sterben als das OOM an die problematische Anwendung zu melden.

Und es ist der Fall, wenn es nicht Ihr Prozess ist, der die Situation geschaffen hat, also hilft das Freigeben von Speicher nicht, wenn der beleidigende Prozess weiter ausläuft.

Aus diesem Grund sind es oft die großen und eingebetteten Systeme, die diese Techniken einsetzen, denn sie haben die Kontrolle über OS und Speicher, um sie zu aktivieren, und die Disziplin / Motivation, sie zu implementieren.


5
2017-12-02 12:18



Es ist nur wiederherstellbar, wenn Sie es fangen und richtig behandeln.

In denselben Fällen versuchte beispielsweise eine Anforderung, viel Speicher zuzuweisen. Es ist ziemlich vorhersehbar und man kann sehr gut damit umgehen.

In vielen Fällen kann es in Multithread-Anwendungen jedoch vorkommen, dass OOE auch im Hintergrundthread auftritt (einschließlich der Erstellung durch System- / Drittanbieterbibliothek). Es ist fast unmöglich, vorherzusagen, und Sie können möglicherweise nicht den Status all Ihrer Threads wiederherstellen.


4
2017-12-02 12:38



Nein. Ein nicht genügend Speicherfehler vom GC sollte im Allgemeinen innerhalb des aktuellen Threads nicht wiederherstellbar sein. (Wiederherstellbare Thread (Benutzer oder Kernel) Erstellung und Beendigung sollte unterstützt werden)

Zu den Counter-Beispielen: Ich arbeite gerade an einem D-Programmiersprachenprojekt, das NVIDIAs CUDA-Plattform für GPU-Computing nutzt. Anstatt den GPU-Speicher manuell zu verwalten, habe ich Proxy-Objekte erstellt, um den D-GC zu nutzen. Wenn also die GPU einen Speichermangelfehler zurückgibt, führe ich eine vollständige Erfassung durch und erhebe nur eine Ausnahme, wenn sie ein zweites Mal fehlschlägt. Aber dies ist nicht wirklich ein Beispiel für die Wiederherstellung des Arbeitsspeichers, es ist mehr eine GC-Integration. Die anderen Beispiele für die Wiederherstellung (Caches, Frei-Listen, Stapel / Hashes ohne automatisches Schrumpfen usw.) sind alle Strukturen, die ihre eigenen Methoden zum Sammeln / Komprimieren von Speicher haben, die von der GC getrennt sind und dazu neigen, nicht lokal zu sein Funktion. So könnten die Leute etwas wie das folgende implementieren:

T new2(T)( lazy T old_new ) {
    T obj;
    try{
        obj = old_new;
    }catch(OutOfMemoryException oome) {
        foreach(compact; Global_List_Of_Delegates_From_Compatible_Objects)
            compact();
        obj = old_new;
    }
    return obj;
}

Dies ist ein gutes Argument dafür, Unterstützung für die Registrierung / Aufhebung der Registrierung von Objekten, die sich selbst sammeln oder komprimieren, zu Garbage Collectors im Allgemeinen hinzuzufügen.


3
2017-12-02 15:26



Im allgemeinen Fall ist es nicht wiederherstellbar.

Wenn Ihr System jedoch eine Art dynamisches Caching enthält, kann ein Out-of-Memory-Handler häufig die ältesten Elemente im Cache (oder sogar den gesamten Cache) ausgeben.

Natürlich müssen Sie sicherstellen, dass der "Dumping" -Prozess keine neuen Speicherzuweisungen erfordert :) Außerdem kann es schwierig sein, die spezifische Zuordnung wiederherzustellen, die fehlgeschlagen ist, es sei denn, Sie können Ihren Cache-Dump-Code direkt am Allokator anschließen Ebene, so dass der Fehler nicht an den Aufrufer weitergegeben wird.


1
2017-12-02 12:03



Es hängt davon ab, was Sie meinen, wenn Sie nicht genug Speicher haben.

Wann malloc() schlägt auf den meisten Systemen fehl, weil Sie keinen Adressraum mehr haben.

Wenn der größte Teil dieses Speichers durch Cachespeicher oder durch MMAP-Regionen belegt ist, können Sie möglicherweise etwas davon zurückgewinnen, indem Sie Ihren Cache freigeben oder die Freigabe erschweren. Allerdings erfordert dies wirklich, dass Sie wissen, wofür Sie diesen Speicher verwenden - und wie Sie festgestellt haben, tun dies die meisten Programme nicht oder es macht keinen Unterschied.

Wenn du benutzt hast setrlimit() auf dich selbst (um dich vor unvorhergesehenen Angriffen zu schützen, vielleicht, oder vielleicht hat root es dir angetan), kannst du das Limit in deinem Error-Handler lockern. Ich mache das sehr häufig - nachdem ich den Benutzer nach Möglichkeit gefragt habe und das Ereignis protokolliert habe.

Auf der anderen Seite ist der Stack-Überlauf etwas schwieriger und nicht portabel. Ich schrieb eine posixish Lösung für ECLund beschrieben eine Windows-Implementierung, wenn Sie diese Route gehen. Es wurde vor ein paar Monaten in ECL eingecheckt, aber ich kann die Original-Patches ausgraben, wenn Sie interessiert sind.


1
2017-12-02 13:05



Die Frage ist mit "sprachunabhängig" markiert, aber sie ist schwer zu beantworten, ohne die Sprache und / oder das zugrundeliegende System zu berücksichtigen. (Ich sehe mehrere toher-hadns

Wenn die Speicherzuweisung implizit ist und kein Mechanismus vorhanden ist, um zu erkennen, ob eine bestimmte Zuweisung erfolgreich war oder nicht, kann das Wiederherstellen aus einer Nichtspeicherbedingung schwierig oder unmöglich sein.

Wenn Sie beispielsweise eine Funktion aufrufen, die versucht, ein riesiges Array zuzuordnen, definieren die meisten Sprachen das Verhalten nur dann nicht, wenn das Array nicht zugeordnet werden kann. (In Ada erhöht dies a Storage_Error Ausnahme, zumindest im Prinzip, und es sollte möglich sein, damit umzugehen.)

Auf der anderen Seite, wenn Sie einen Mechanismus haben, der versucht, Speicher zuzuweisen und in der Lage ist, einen Fehler zu melden (wie C's) malloc() oder C ++ new), dann ist es ja möglich, sich von diesem Fehler zu erholen. Zumindest in den Fällen von malloc() und newBei einer fehlgeschlagenen Zuordnung wird nur ein Fehler gemeldet (z. B. werden keine internen Datenstrukturen beschädigt).

Ob es sinnvoll ist, eine Wiederherstellung zu versuchen, hängt von der Anwendung ab. Wenn die Anwendung nach einem Zuteilungsfehler nicht erfolgreich sein kann, sollte sie die Bereinigung durchführen und beenden. Aber wenn der Zuordnungsfehler lediglich bedeutet, dass eine bestimmte Aufgabe nicht ausgeführt werden kann, oder wenn die Aufgabe mit weniger Arbeitsspeicher noch langsamer ausgeführt werden kann, ist es sinnvoll, weiter zu arbeiten.

Ein konkretes Beispiel: Angenommen, ich verwende einen Texteditor. Wenn ich versuche, eine Operation innerhalb des Editors auszuführen, die viel Speicher benötigt, und diese Operation nicht ausgeführt werden kann, möchte ich, dass der Editor mir sagt, dass er nicht tun kann, was ich gefragt habe und lass mich weiter bearbeiten. Die Kündigung ohne Speichern meiner Arbeit wäre eine inakzeptable Antwort. Speichern meiner Arbeit und Beenden wäre besser, aber immer noch unnötig benutzerfeindlich.


1
2017-08-05 19:02