Dokumentation für java.lang.Error
sagt:
Ein Fehler ist eine Unterklasse von Throwable, die auf schwerwiegende Probleme hinweist, die eine vernünftige Anwendung nicht abfangen sollte
Aber wie java.lang.Error
es eine Unterklasse von ist java.lang.Throwable
, kann ich diese Art von Throwable fangen.
Ich verstehe, warum es keine gute Idee ist, diese Art von Ausnahme zu erwischen. Soweit ich weiß, sollte der Catch-Handler keinen Speicher selbst zuweisen, wenn wir uns dazu entschließen, ihn abzufangen. Sonst OutOfMemoryError
wird wieder geworfen.
Meine Frage lautet also:
- Gibt es reale Szenarien, in denen das Fangen
java.lang.OutOfMemoryError
eine gute Idee sein könnte? java.lang.OutOfMemoryError
Wie können wir sicherstellen, dass der Catch-Handler keinen eigenen Speicher zuweist (Tools oder Best Practices), wenn wir uns für den Fang entscheiden ?
java
try-catch
out-of-memory
Denis Bazhenov
quelle
quelle
Antworten:
Ich stimme den meisten Antworten hier zu und stimme ihnen nicht zu.
Es gibt eine Reihe von Szenarien, in denen Sie eine
OutOfMemoryError
und nach meiner Erfahrung (unter Windows- und Solaris-JVMs) nur sehr seltenOutOfMemoryError
den Todesstoß für eine JVM einfangen möchten .Es gibt nur einen guten Grund, einen zu fangen,
OutOfMemoryError
und zwar, ihn ordnungsgemäß zu schließen, Ressourcen sauber freizugeben und den Grund für den Fehler so gut wie möglich zu protokollieren (sofern dies noch möglich ist).Im Allgemeinen
OutOfMemoryError
tritt dies aufgrund einer Blockspeicherzuordnung auf, die mit den verbleibenden Ressourcen des Heaps nicht zufrieden sein kann.Wenn der
Error
geworfen wird, enthält der Heap die gleiche Menge zugeordneter Objekte wie vor der nicht erfolgreichen Zuweisung. Jetzt ist es an der Zeit, Verweise auf Laufzeitobjekte zu löschen, um noch mehr Speicher freizugeben, der möglicherweise für die Bereinigung erforderlich ist. In diesen Fällen kann es sogar möglich sein, fortzufahren, aber das wäre definitiv eine schlechte Idee, da Sie niemals 100% sicher sein können, dass sich die JVM in einem reparierbaren Zustand befindet.Demonstration,
OutOfMemoryError
die nicht bedeutet, dass die JVM im catch-Block nicht genügend Speicher hat:Ausgabe dieses Codes:
Wenn etwas Kritisches ausgeführt wird, fange ich das normalerweise ab
Error
, protokolliere es bei syserr, protokolliere es dann mit dem Protokollierungsframework Ihrer Wahl, setze Ressourcen frei und schließe auf saubere Weise. Was ist das Schlimmste, was passieren kann? Die JVM stirbt ohnehin (oder ist bereits tot) und durch das FangenError
besteht zumindest die Möglichkeit einer Bereinigung.Die Einschränkung besteht darin, dass Sie diese Fehler nur an Stellen abfangen müssen, an denen eine Bereinigung möglich ist. Decken Sie nicht
catch(Throwable t) {}
überall oder Unsinn so.quelle
OutOfMemoryError
möglicherweise von einem Punkt aus ausgelöst wurde, der Ihr Programm in einen inkonsistenten Zustand versetzt hat, weil sie jederzeit ausgelöst werden kann. Siehe stackoverflow.com/questions/8728866/…Sie können sich davon erholen:
Aber macht es Sinn? Was möchten Sie noch tun? Warum würden Sie anfänglich so viel Speicher zuweisen? Ist weniger Speicher auch in Ordnung? Warum nutzt du es nicht schon? Oder wenn dies nicht möglich ist, warum nicht einfach der JVM von Anfang an mehr Speicherplatz geben?
Zurück zu Ihren Fragen:
Niemand fällt mir ein.
Kommt darauf an, was das OOME verursacht hat. Wenn es außerhalb des
try
Blocks deklariert ist und Schritt für Schritt passiert ist, sind Ihre Chancen gering. Möglicherweise möchten Sie vorher etwas Speicherplatz reservieren:und setzen Sie es dann während OOME auf Null:
Natürlich macht das alles mit allem keinen Sinn;) Geben Sie JVM einfach genügend Speicher, wie es Ihre Anwendung erfordert. Führen Sie bei Bedarf einen Profiler aus.
quelle
null
?Im Allgemeinen ist es eine schlechte Idee, zu versuchen, einen OOM zu fangen und sich von ihm zu erholen.
Ein OOME könnte auch auf andere Threads geworfen worden sein, einschließlich Threads, von denen Ihre Anwendung nicht einmal weiß. Solche Threads sind jetzt tot, und alles, was auf eine Benachrichtigung wartete, konnte für immer hängen bleiben. Kurz gesagt, Ihre App könnte endgültig defekt sein.
Selbst wenn Sie sich erfolgreich erholen, leidet Ihre JVM möglicherweise immer noch unter Heap-Hunger und Ihre Anwendung wird infolgedessen eine miserable Leistung erbringen.
Das Beste, was man mit einem OOME machen kann, ist, die JVM sterben zu lassen.
(Dies setzt voraus , dass die JVM tut sterben. Zum Beispiel Ooms auf einem Tomcat - Servlet - Thread tötet nicht die JVM, und dies führt zu dem Tomcat geht in einen katatonischen Zustand , in dem er nicht antworten auf alle Anfragen ... nicht einmal Anfragen an Neustart.)
BEARBEITEN
Ich sage nicht, dass es eine schlechte Idee ist, OOM überhaupt zu fangen. Die Probleme treten auf, wenn Sie dann versuchen, sich absichtlich oder durch Versehen von der OOME zu erholen. Wann immer Sie ein OOM abfangen (direkt oder als Subtyp von Error oder Throwable), sollten Sie es entweder erneut werfen oder veranlassen, dass die Anwendung / JVM beendet wird.
Nebenbei: Dies legt nahe, dass eine Anwendung für maximale Robustheit gegenüber OOMs Thread.setDefaultUncaughtExceptionHandler () verwenden sollte , um einen Handler festzulegen, der bewirkt, dass die Anwendung im Falle eines OOME beendet wird, unabhängig davon, auf welchen Thread das OOME geworfen wird. Ich würde mich für Meinungen zu diesem Thema interessieren ...
Das einzige andere Szenario ist, wenn Sie sicher wissen , dass der OOM keinen Kollateralschaden verursacht hat. dh du weißt:
Es gibt Anwendungen, bei denen es möglich ist, diese Dinge zu wissen, aber für die meisten Anwendungen können Sie nicht sicher sein, dass die Fortsetzung nach einem OOME sicher ist. Auch wenn es empirisch "funktioniert", wenn Sie es versuchen.
(Das Problem ist, dass ein formeller Nachweis erforderlich ist, um zu zeigen, dass die Konsequenzen "erwarteter" OOMEs sicher sind und dass "unerwartete" OOME nicht unter der Kontrolle eines Try / Catch-OOME auftreten können.)
quelle
Error
.Ja, es gibt reale Szenarien. Hier ist meine: Ich muss Datensätze von sehr vielen Elementen in einem Cluster mit begrenztem Speicher pro Knoten verarbeiten. Eine bestimmte JVM-Instanz durchläuft viele Elemente nacheinander, aber einige Elemente sind zu groß, um im Cluster verarbeitet zu werden: Ich kann das abfangen
OutOfMemoryError
und notieren, welche Elemente zu groß sind. Später kann ich nur die großen Elemente auf einem Computer mit mehr RAM erneut ausführen.(Da es sich um eine einzelne Multi-Gigabyte-Zuweisung eines Arrays handelt, die fehlschlägt, ist die JVM nach dem Erkennen des Fehlers immer noch in Ordnung und es ist genügend Speicher vorhanden, um die anderen Elemente zu verarbeiten.)
quelle
byte[] bytes = new byte[length]
? Warum nicht einfachsize
zu einem früheren Zeitpunkt nachsehen?size
mit mehr Speicher gut sein wird. Ich gehe über die Ausnahme, weil in den meisten Fällen alles in Ordnung sein wird.Es gibt definitiv Szenarien, in denen es Sinn macht, ein OOME zu fangen. IDEA fängt sie ab und öffnet ein Dialogfeld, in dem Sie die Einstellungen für den Startspeicher ändern können (und wird dann beendet, wenn Sie fertig sind). Ein Anwendungsserver kann sie abfangen und melden. Der Schlüssel dazu besteht darin, dies beim Versand auf einem hohen Niveau zu tun, damit Sie eine vernünftige Chance haben, an dem Punkt, an dem Sie die Ausnahme abfangen, eine Reihe von Ressourcen freizusetzen.
Neben dem obigen IDEA-Szenario sollte der Fang im Allgemeinen von Throwable sein, nicht nur von OOM, und in einem Kontext, in dem zumindest der Thread in Kürze beendet wird.
Natürlich ist das Gedächtnis meistens ausgehungert und die Situation kann nicht wiederhergestellt werden, aber es gibt Möglichkeiten, wie es Sinn macht.
quelle
Ich bin auf diese Frage gestoßen, weil ich mich gefragt habe, ob es in meinem Fall eine gute Idee ist, OutOfMemoryError zu fangen. Ich antworte hier teilweise, um ein weiteres Beispiel zu zeigen, wenn das Abfangen dieses Fehlers für jemanden (dh mich) sinnvoll sein kann, und teilweise, um herauszufinden, ob es in meinem Fall tatsächlich eine gute Idee ist (da ich ein überaus junger Entwickler bin, kann ich das nie Seien Sie zu sicher über jede einzelne Codezeile, die ich schreibe.
Wie auch immer, ich arbeite an einer Android-Anwendung, die auf verschiedenen Geräten mit unterschiedlichen Speichergrößen ausgeführt werden kann. Der gefährliche Teil besteht darin, eine Bitmap aus einer Datei zu dekodieren und in einer ImageView-Instanz anzuzeigen. Ich möchte die leistungsstärkeren Geräte nicht auf die Größe der dekodierten Bitmap beschränken und kann auch nicht sicher sein, dass die App nicht auf einem alten Gerät ausgeführt wird, auf das ich noch nie mit sehr wenig Speicher gestoßen bin. Daher mache ich das:
Auf diese Weise schaffe ich es, mehr und weniger leistungsstarke Geräte bereitzustellen, die den Bedürfnissen und Erwartungen ihrer Benutzer entsprechen.
quelle
Ja, die eigentliche Frage lautet: "Was werden Sie im Ausnahmebehandler tun?" Für fast alles Nützliche weisen Sie mehr Speicher zu. Wenn Sie beim Auftreten eines OutOfMemoryError Diagnosearbeiten durchführen möchten, können Sie den
-XX:OnOutOfMemoryError=<cmd>
von der HotSpot-VM bereitgestellten Hook verwenden. Es führt Ihre Befehle aus, wenn ein OutOfMemoryError auftritt, und Sie können etwas Nützliches außerhalb von Javas Heap tun. Sie möchten wirklich verhindern, dass der Anwendung der Speicherplatz ausgeht. Daher ist es der erste Schritt, herauszufinden, warum dies geschieht. Anschließend können Sie die Heap-Größe von MaxPermSize entsprechend erhöhen. Hier sind einige andere nützliche HotSpot-Hooks:Die vollständige Liste finden Sie hier
quelle
OutOfMemeoryError
an jedem Punkt in Ihrem Programm (nicht nur aus einernew
Anweisung) ein geworfen werden kann, befindet sich Ihr Programm in einem undefinierten Zustand, wenn Sie die Exktion abfangen.Ich habe eine Anwendung, die nach OutOfMemoryError-Fehlern wiederhergestellt werden muss. In Single-Thread-Programmen funktioniert sie immer, in Multithread-Programmen jedoch manchmal nicht. Die Anwendung ist ein automatisiertes Java-Testtool, das generierte Testsequenzen in Testklassen bis zur maximal möglichen Tiefe ausführt. Jetzt muss die Benutzeroberfläche stabil sein, aber der Test-Engine kann der Speicherplatz ausgehen, während der Baum der Testfälle vergrößert wird. Ich behandle dies mit der folgenden Art von Code-Idiom in der Test-Engine:
Dies funktioniert jedes Mal in Single-Thread-Anwendungen. Aber ich habe kürzlich meine Test-Engine in einen separaten Worker-Thread von der Benutzeroberfläche gestellt. Jetzt kann der Speichermangel in beiden Threads willkürlich auftreten, und mir ist nicht klar, wie ich ihn abfangen soll.
Ich habe beispielsweise OOME auftreten lassen, während die Frames eines animierten GIF in meiner Benutzeroberfläche von einem proprietären Thread durchlaufen wurden, der hinter den Kulissen von einer Swing-Klasse erstellt wird, die außerhalb meiner Kontrolle liegt. Ich hatte gedacht, ich hätte alle benötigten Ressourcen im Voraus zugewiesen, aber der Animator weist eindeutig jedes Mal Speicher zu, wenn er das nächste Bild abruft. Wenn jemand eine Idee hat, wie man mit OOMEs umgeht, die in einem Thread ausgelöst wurden , würde ich gerne hören.
quelle
OOME kann abgefangen werden, ist jedoch im Allgemeinen nutzlos, je nachdem, ob die JVM in der Lage ist, einige Objekte zu sammeln, wenn der Fang erreicht ist, und wie viel Heapspeicher bis zu diesem Zeitpunkt noch vorhanden ist.
Beispiel: In meiner JVM wird dieses Programm vollständig ausgeführt:
Wenn Sie jedoch nur eine einzelne Zeile in den Fang einfügen, sehen Sie, wovon ich spreche:
Das erste Programm läuft einwandfrei, da die JVM bei Erreichen des Catch erkennt, dass die Liste nicht mehr verwendet wird (diese Erkennung kann auch eine Optimierung sein, die zur Kompilierungszeit vorgenommen wird). Wenn wir also die print-Anweisung erreichen, wurde der Heap-Speicher fast vollständig freigegeben, sodass wir jetzt einen großen Handlungsspielraum haben, um fortzufahren. Dies ist der beste Fall.
Wenn der Code jedoch so angeordnet ist, dass die Liste
ll
nach dem Abfangen des OOME verwendet wird, kann die JVM ihn nicht erfassen. Dies geschieht im zweiten Snippet. Das OOME, ausgelöst durch eine neue Long-Erstellung, wird abgefangen, aber bald erstellen wir ein neues Objekt (eine Zeichenfolge in derSystem.out,println
Zeile), und der Heap ist fast voll, sodass ein neues OOME ausgelöst wird. Dies ist der schlimmste Fall: Wir haben versucht, ein neues Objekt zu erstellen, wir sind fehlgeschlagen, wir haben das OOME abgefangen, ja, aber jetzt löst die erste Anweisung, die neuen Heapspeicher benötigt (z. B. das Erstellen eines neuen Objekts), ein neues OOME aus. Denken Sie darüber nach, was können wir an diesem Punkt noch tun, wenn so wenig Speicher übrig bleibt? Wahrscheinlich nur aufregend. Daher das Nutzlose.Einer der Gründe, warum die JVM keine Ressourcen sammelt, ist wirklich beängstigend: Eine gemeinsam genutzte Ressource, die auch von anderen Threads verwendet wird. Jeder mit einem Gehirn kann sehen, wie gefährlich das Fangen von OOME sein kann, wenn es in eine nicht experimentelle App jeglicher Art eingefügt wird.
Ich verwende eine Windows x86 32-Bit-JVM (JRE6). Der Standardspeicher für jede Java-App beträgt 64 MB.
quelle
ll=null
im Fangblock bin ?Der einzige Grund, warum ich mir vorstellen kann, warum das Abfangen von OOM-Fehlern sein könnte, ist, dass Sie einige massive Datenstrukturen haben, die Sie nicht mehr verwenden, und auf null setzen und etwas Speicher freigeben können. Aber (1) das bedeutet, dass Sie Speicher verschwenden, und Sie sollten Ihren Code reparieren, anstatt nur nach einem OOME zu humpeln, und (2) was würden Sie tun, selbst wenn Sie ihn abfangen würden? OOM kann jederzeit auftreten und möglicherweise alles zur Hälfte erledigen.
quelle
Für die Frage 2 sehe ich bereits die Lösung, die ich von BalusC vorschlagen würde.
Ich glaube, ich bin gerade auf ein gutes Beispiel gestoßen. Wenn eine awt-Anwendung Nachrichten versendet, wird der nicht abgefangene OutOfMemoryError auf stderr angezeigt und die Verarbeitung der aktuellen Nachricht wird gestoppt. Aber die Anwendung läuft weiter! Der Benutzer kann weiterhin andere Befehle ausgeben, ohne die schwerwiegenden Probleme zu kennen, die hinter den Kulissen auftreten. Besonders wenn er den Standardfehler nicht beobachten kann oder nicht. Daher ist es wünschenswert, eine Ausnahme zu erwischen und einen Neustart der Anwendung bereitzustellen (oder zumindest vorzuschlagen).
quelle
Ich habe nur ein Szenario, in dem das Abfangen eines OutOfMemoryError sinnvoll erscheint und zu funktionieren scheint.
Szenario: In einer Android-App möchte ich mehrere Bitmaps in höchstmöglicher Auflösung anzeigen und sie flüssig zoomen können.
Aufgrund des fließenden Zooms möchte ich die Bitmaps im Speicher haben. Android hat jedoch Speicherbeschränkungen, die geräteabhängig und schwer zu steuern sind.
In dieser Situation kann es beim Lesen der Bitmap zu OutOfMemoryError kommen. Hier hilft es, wenn ich es fange und dann mit niedrigerer Auflösung weitermache.
quelle
OutOfMemory
passiert das jetzt nicht aufgrund eines nicht verwandten Fixes). Selbst wenn Sie es abfangen, ist möglicherweise ein wichtiger Code beschädigt: Wenn Sie mehrere Threads haben, kann die Speicherzuweisung in einem dieser Threads fehlschlagen. Abhängig von Ihrer Anwendung besteht also immer noch eine Wahrscheinlichkeit von 10 bis 90%, dass sie irreversibel beschädigt wird.EDIT: Ich schlage vor, Sie probieren es aus. Schreiben Sie beispielsweise ein Programm, das rekursiv eine Funktion aufruft, die zunehmend mehr Speicher zuweist. Fangen Sie
OutOfMemoryError
und sehen Sie, ob Sie von diesem Punkt aus sinnvoll weitermachen können. Nach meiner Erfahrung werden Sie in der Lage sein, obwohl dies in meinem Fall unter dem WebLogic-Server geschehen ist, sodass möglicherweise schwarze Magie beteiligt war.quelle
Sie können alles unter Throwable abfangen. Im Allgemeinen sollten Sie nur Unterklassen von Exception mit Ausnahme von RuntimeException abfangen (obwohl ein großer Teil der Entwickler auch RuntimeException abfängt ... aber das war nie die Absicht der Sprachdesigner).
Wenn Sie OutOfMemoryError fangen würden, was um alles in der Welt würden Sie tun? Die VM verfügt nicht über genügend Arbeitsspeicher. Grundsätzlich können Sie nur beenden. Sie können wahrscheinlich nicht einmal ein Dialogfeld öffnen, um ihnen mitzuteilen, dass Sie nicht genügend Speicher haben, da dies Speicherplatz beanspruchen würde :-)
Die VM löst einen OutOfMemoryError aus, wenn wirklich nicht genügend Arbeitsspeicher vorhanden ist (tatsächlich sollten alle Fehler auf nicht behebbare Situationen hinweisen), und es sollte wirklich nichts geben, was Sie tun können, um damit umzugehen.
Sie müssen herausfinden, warum Ihnen der Speicher ausgeht (verwenden Sie einen Profiler wie den in NetBeans) und sicherstellen, dass keine Speicherlecks auftreten. Wenn Sie keine Speicherlecks haben, erhöhen Sie den Speicher, den Sie der VM zuweisen.
quelle