Wie gehe ich mit dem Fehler "java.lang.OutOfMemoryError: Java Heap Space" um?

416

Ich schreibe eine clientseitige Swing- Anwendung (Graphical Font Designer) auf Java 5 . Vor kurzem bin ich auf einen java.lang.OutOfMemoryError: Java heap spaceFehler gestoßen, weil ich bei der Speichernutzung nicht konservativ bin. Der Benutzer kann eine unbegrenzte Anzahl von Dateien öffnen, und das Programm speichert die geöffneten Objekte im Speicher. Nach einer kurzen Recherche fand ich Ergonomie in der 5.0 Java Virtual Machine und anderen, die auf einem Windows-Computer sagten, die JVM sei standardmäßig die maximale Heap-Größe 64MB.

Wie soll ich in dieser Situation mit dieser Einschränkung umgehen?

Ich könnte die maximale Größe des Heapspeichers mithilfe der Befehlszeilenoption für Java erhöhen , aber dazu müsste der verfügbare Arbeitsspeicher ermittelt und ein Startprogramm oder ein Skript geschrieben werden. Außerdem wird das Problem letztendlich nicht beseitigt, wenn man es auf ein endliches Maximum erhöht .

Ich könnte einen Teil meines Codes neu schreiben, um Objekte häufig im Dateisystem zu speichern (die Verwendung der Datenbank ist dasselbe), um den Speicher freizugeben. Es könnte funktionieren, aber es ist wahrscheinlich auch viel Arbeit.

Wenn Sie mich auf Details der oben genannten Ideen oder auf Alternativen wie den automatischen virtuellen Speicher hinweisen könnten , der die Größe des Heapspeichers dynamisch erweitert , wäre das großartig.

Eugene Yokota
quelle
Die standardmäßige maximale Heap-Größe von 64 MB liegt vor J2SE 5.0. Informationen zu J2SE 8.0 finden Sie unter "Garbage Collector Ergonomics" unter docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas
Wenn Sie hier gelandet sind, weil jede OOM-Frage auf diese getäuscht wurde, lesen Sie auch Folgendes : stackoverflow.com/questions/299659/… Es bietet die Lösung zum Bereinigen von Speicherreferenzen "just in time" vor dem OOM. SoftReferences ist möglicherweise das Tool, mit dem Sie Ihr eigentliches Problem lösen können.
Steve Steiner

Antworten:

244

Letztendlich haben Sie immer ein begrenztes Maximum an Heap, das Sie verwenden können, unabhängig davon, auf welcher Plattform Sie ausgeführt werden. In Windows 32 Bit ist dies ungefähr so 2GB(nicht speziell Heap, sondern Gesamtmenge an Speicher pro Prozess). Es kommt nur vor, dass Java die Standardeinstellung verkleinert (vermutlich, damit der Programmierer keine Programme mit außer Kontrolle geratener Speicherzuordnung erstellen kann, ohne auf dieses Problem zu stoßen und genau untersuchen zu müssen, was sie tun).

Angesichts der Tatsache, dass Sie verschiedene Ansätze wählen können, um entweder zu bestimmen, welche Speichermenge Sie benötigen, oder um die von Ihnen verwendete Speichermenge zu reduzieren. Ein häufiger Fehler bei durch Müll gesammelten Sprachen wie Java oder C # besteht darin, Verweise auf Objekte, die Sie nicht mehr verwenden, beizubehalten oder viele Objekte zuzuweisen, wenn Sie sie stattdessen wiederverwenden könnten. Solange Objekte einen Verweis auf sie haben, verwenden sie weiterhin Heap-Speicherplatz, da der Garbage Collector sie nicht löscht.

In diesem Fall können Sie einen Java-Speicherprofiler verwenden, um zu bestimmen, welche Methoden in Ihrem Programm eine große Anzahl von Objekten zuweisen, und dann zu bestimmen, ob es eine Möglichkeit gibt, sicherzustellen, dass auf sie nicht mehr verwiesen wird, oder sie überhaupt nicht zuzuweisen. Eine Option, die ich in der Vergangenheit verwendet habe, ist "JMP" http://www.khelekore.org/jmp/ .

Wenn Sie feststellen, dass Sie diese Objekte aus einem bestimmten Grund zuweisen und Referenzen beibehalten müssen (je nachdem, was Sie tun, kann dies der Fall sein), müssen Sie beim Starten des Programms nur die maximale Heap-Größe erhöhen. Sobald Sie jedoch die Speicherprofilerstellung durchgeführt haben und wissen, wie Ihre Objekte zugewiesen werden, sollten Sie eine bessere Vorstellung davon haben, wie viel Speicher Sie benötigen.

Wenn Sie nicht garantieren können, dass Ihr Programm in einer begrenzten Menge an Speicher ausgeführt wird (möglicherweise abhängig von der Eingabegröße), tritt dieses Problem im Allgemeinen immer auf. Erst wenn Sie all dies ausgeschöpft haben, müssen Sie sich mit dem Zwischenspeichern von Objekten auf der Festplatte usw. befassen. An diesem Punkt sollten Sie einen sehr guten Grund haben, für etwas "Ich benötige XGB Speicher" zu sagen, und Sie können es nicht durch Verbesserung umgehen Ihre Algorithmen oder Speicherzuweisungsmuster. Im Allgemeinen ist dies normalerweise nur bei Algorithmen der Fall, die mit großen Datenmengen (wie einer Datenbank oder einem wissenschaftlichen Analyseprogramm) arbeiten, und dann werden Techniken wie Caching und speicherabgebildete E / A nützlich.

Ben Childs
quelle
6
OpenJDK und OracleJDK haben den Profiler jvisualvm gebündelt. Wenn Sie mehr Komfort wünschen, würde ich kommerzielles Yourkit vorschlagen.
Petr Gladkikh
121

Führen Sie Java mit der Befehlszeilenoption aus -Xmx, mit der die maximale Größe des Heapspeichers festgelegt wird.

Siehe hier für Details .

Dave Webb
quelle
3
Wie kann man diesen Parameter für immer einstellen? Weil ich den Befehl 'gradlew assemble' verwende.
Dr.jacky
2
Ausführen-> Konfigurationen ausführen-> Klicken Sie auf Argumente-> innerhalb der VM-Argumente Typ -Xms1g -Xmx2g
Arayan Singh
2
Das ist die wahre Antwort.
NCCC
85

Sie können pro Projekt angeben , wie viel Heapspeicher Ihr Projekt benötigt

Folgendes gilt für Eclipse Helios / Juno / Kepler :

Klicken Sie mit der rechten Maustaste auf

 Run As - Run Configuration - Arguments - Vm Arguments, 

dann füge dies hinzu

-Xmx2048m
allenhwkim
quelle
1
hi bighostkim und cuongHuyTo, wo ist "Argumente" .. ich kann bis zu Run Configuration sehen. Bitte rufen Sie mich an. Ich muss fast 2000 Kontakte von Google Mail herunterladen und speichern. Es stürzt wegen Speicherausfall ab
AndroidRaji
@AndroiRaji: Klicken Sie mit der rechten Maustaste auf die Java-Klasse mit einem ausführbaren Main (dh "public static void main (String [] args)") und wählen Sie dann Ausführen als - Konfiguration ausführen. Dann ist "Argumente" die Registerkarte direkt nach dem Main (Sie sehen die Registerkarten Main, Argumente, JRE, Klassenpfad, Quelle, Umgebung, Allgemein).
CuongHuyTo
47

Das Erhöhen der Heap-Größe ist kein "Fix", sondern ein "Gips", 100% vorübergehend. Es wird wieder woanders abstürzen. Um diese Probleme zu vermeiden, schreiben Sie Hochleistungscode.

  1. Verwenden Sie nach Möglichkeit lokale Variablen.
  2. Stellen Sie sicher, dass Sie das richtige Objekt auswählen (EX: Auswahl zwischen String, StringBuffer und StringBuilder)
  3. Verwenden Sie ein gutes Codesystem für Ihr Programm (EX: Verwenden statischer Variablen im Vergleich zu nicht statischen Variablen)
  4. Andere Sachen, die an Ihrem Code arbeiten könnten.
  5. Versuchen Sie, sich mit Multy THREADING zu bewegen
Zitronensaft
quelle
Das ist so wahr. Ich versuche, ein Problem zu beheben, bei dem ich OOM für AWT-Thread erhalte, aber wenn ich einen anderen neuen Thread verwende, erhalte ich kein OOM-Problem. Alles, was ich online finden kann, ist die Erhöhung der Heap-Größe für AWT-Threads.
Ashish
@Ash: Ja, beheben Sie das Kernproblem, anstatt nach Pflastern zu suchen.
Zitronensaft
Die Speicherbereinigung und der Speicherverwaltungsansatz in Java sollten all diese Malloc-Dealloc-Komplikationen seiner Vorgänger lösen :( Natürlich stimme ich dieser Antwort voll und ganz zu. Es ist nur eine Schande, dass die Standardeinstellungen das Schreiben von Code mit schlanken Daten nicht einfach machen -Strukturen, die so schnell wie möglich aufgeräumt werden.
Davos
31

Große Einschränkung ---- In meinem Büro stellten wir fest, dass wir (auf einigen Windows-Computern) nicht mehr als 512 m für Java-Heap zuweisen konnten. Dies war auf das auf einigen dieser Computer installierte Antivirenprodukt von Kaspersky zurückzuführen. Nach der Deinstallation dieses AV-Produkts haben wir festgestellt, dass wir mindestens 1,6 GB zuweisen können, dh -Xmx1600m(m ist obligatorisch, andernfalls führt dies zu einem weiteren Fehler "Zu kleiner anfänglicher Heap").

Keine Ahnung, ob dies bei anderen AV-Produkten der Fall ist, aber vermutlich geschieht dies, weil das AV-Programm in jedem Adressraum einen kleinen Speicherblock reserviert und so eine einzelne wirklich große Zuordnung verhindert.

David
quelle
22

VM-Argumente haben bei mir in Eclipse funktioniert. Wenn Sie Eclipse Version 3.4 verwenden, gehen Sie wie folgt vor

Gehen Sie zu Run --> Run Configurations -->und wählen Sie das Projekt unter Maven Build aus -> wählen Sie dann die Registerkarte "JRE" -> und geben Sie ein -Xmx1024m.

Alternativ können Sie Run --> Run Configurations --> select the "JRE" tab -->dann eingeben -Xmx1024m

Dies sollte den Speicherheap für alle Builds / Projekte erhöhen. Die oben angegebene Speichergröße beträgt 1 GB. Sie können nach Ihren Wünschen optimieren.

Liebe alle
quelle
18

Ja, mit können -XmxSie mehr Speicher für Ihre JVM konfigurieren. Um sicherzugehen, dass Sie keinen Speicher verlieren oder verschwenden. Machen Sie einen Heap-Dump und analysieren Sie Ihren Speicherverbrauch mit dem Eclipse Memory Analyzer .

kohlerm
quelle
JVMJ9VM007E Befehlszeilenoption nicht erkannt: -Xmx Die virtuelle Java-Maschine konnte nicht erstellt werden. Downvote
Philip Rego
17

Ich möchte Empfehlungen aus dem Artikel zur Orakel- Fehlerbehebung hinzufügen .

Ausnahme im Thread thread_name: java.lang.OutOfMemoryError: Java- Heapspeicher

Die Detailmeldung Java-Heap-Speicherplatz gibt an, dass das Objekt im Java-Heap nicht zugeordnet werden konnte. Dieser Fehler impliziert nicht unbedingt einen Speicherverlust

Mögliche Ursachen:

  1. Einfaches Konfigurationsproblem , bei dem die angegebene Heap-Größe für die Anwendung nicht ausreicht.

  2. Die Anwendung enthält unbeabsichtigt Verweise auf Objekte , und dies verhindert, dass die Objekte durch Müll gesammelt werden.

  3. Übermäßiger Einsatz von Finalisierern .

Eine weitere mögliche Ursache für diesen Fehler sind Anwendungen, bei denen Finalizer übermäßig verwendet werden. Wenn eine Klasse über eine Finalisierungsmethode verfügt, wird der Speicherplatz von Objekten dieses Typs zum Zeitpunkt der Speicherbereinigung nicht zurückgefordert

Nach der Speicherbereinigung werden die Objekte zur Finalisierung in die Warteschlange gestellt , was zu einem späteren Zeitpunkt erfolgt. Finalizer werden von einem Daemon-Thread ausgeführt, der die Finalisierungswarteschlange bedient. Wenn der Finalizer- Thread nicht mit der Finalisierungswarteschlange Schritt halten kann, kann sich der Java-Heap füllen und diese Art von OutOfMemoryError- Ausnahme wird ausgelöst.

Ein Szenario, das diese Situation verursachen kann, besteht darin, dass eine Anwendung Threads mit hoher Priorität erstellt , die dazu führen, dass die Finalisierungswarteschlange schneller ansteigt als die Rate, mit der der Finalizer-Thread diese Warteschlange bedient.

Ravindra Babu
quelle
9

Befolgen Sie die folgenden Schritte:

  1. Öffnen Sie catalina.shvon Tomcat / Bin.

  2. Ändern Sie JAVA_OPTS in

    JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m 
    -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m 
    -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
  3. Starten Sie Ihren Kater neu

Pradip Bhatt
quelle
8

Ich habe an einer anderen Stelle gelesen, dass Sie es versuchen können - catch java.lang.OutOfMemoryError. Auf dem catch-Block können Sie alle Ressourcen freigeben, von denen Sie wissen, dass sie viel Speicher verbrauchen, Verbindungen schließen usw. und dann alles System.gc()erneut versuchen du wolltest es tun.

Ein anderer Weg ist dies, obwohl ich nicht weiß, ob dies funktionieren würde, aber ich teste gerade, ob es auf meiner Anwendung funktionieren wird.

Die Idee ist, die Garbage Collection durch Aufrufen von System.gc () durchzuführen, von dem bekannt ist, dass es den freien Speicher erhöht. Sie können dies weiter überprüfen, nachdem ein Speicherfresscode ausgeführt wurde.

//Mimimum acceptable free memory you think your app needs
long minRunningMemory = (1024*1024);

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();
mwangi
quelle
6
Im Allgemeinen denke ich, dass die JVM Garbage Collect (GC) vorziehen wird, anstatt einen OutOfMemoryError auszulösen. Das explizite Aufrufen von System.gc () nach OutOfMemoryError kann möglicherweise bei einigen VMs / Konfigurationen hilfreich sein, aber ich würde nicht erwarten, dass es im allgemeinen Fall sehr gut funktioniert. Das Löschen unnötiger Objektreferenzen würde jedoch in fast allen Fällen definitiv helfen.
Mike Clark
6
@mwangi Das Aufrufen von System.gc () direkt aus dem Code ist im Allgemeinen eine schlechte Idee. Es ist nur ein Vorschlag an JVM, dass GC durchgeführt werden sollte, aber es gibt absolut keine Garantie dafür, dass es durchgeführt wird.
7

Eine einfache Möglichkeit, OutOfMemoryErrorin Java zu lösen, besteht darin, die maximale Heap-Größe mithilfe von JVM-Optionen zu -Xmx512Merhöhen. Dadurch wird Ihr OutOfMemoryError sofort gelöst. Dies ist meine bevorzugte Lösung, wenn ich beim Erstellen eines Projekts OutOfMemoryError in Eclipse, Maven oder ANT erhalte, da Ihnen je nach Projektgröße leicht der Speicher ausgehen kann.

Hier ist ein Beispiel für die Erhöhung der maximalen Heap-Größe von JVM. Außerdem ist es besser, das Verhältnis -Xmx zu -Xms entweder 1: 1 oder 1: 1,5 beizubehalten, wenn Sie die Heap-Größe in Ihrer Java-Anwendung festlegen.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Referenzlink

Chaukssey
quelle
1
Irgendeine Idee, warum wir sie im Verhältnis 1: 1 oder 1: 1,5 halten müssen?
ernesto
7

Standardmäßig verwendet JVM für die Entwicklung kleine und kleine Konfigurationen für andere leistungsbezogene Funktionen. Aber für die Produktion können Sie zB optimieren (zusätzlich kann es eine anwendungsserverspezifische Konfiguration geben) -> (Wenn immer noch nicht genügend Speicher vorhanden ist, um die Anforderung zu erfüllen, und der Heap bereits die maximale Größe erreicht hat, tritt ein OutOfMemoryError auf)

-Xms<size>        set initial Java heap size
-Xmx<size>        set maximum Java heap size
-Xss<size>        set java thread stack size

-XX:ParallelGCThreads=8
-XX:+CMSClassUnloadingEnabled
-XX:InitiatingHeapOccupancyPercent=70
-XX:+UnlockDiagnosticVMOptions
-XX:+UseConcMarkSweepGC
-Xms512m
-Xmx8192m
-XX:MaxPermSize=256m (in java 8 optional)

Zum Beispiel: Unter Linux Platform für den Produktionsmodus bevorzugte Einstellungen.

Nach dem Herunterladen und Konfigurieren des Servers auf diese Weise http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1.Erstellen Sie die Datei setenv.sh im Ordner / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2.Öffnen und schreiben Sie diese Parameter, um den bevorzugten Modus einzustellen.

nano  /opt/tomcat/bin/setenv.sh 

export CATALINA_OPTS="$CATALINA_OPTS -XX:ParallelGCThreads=8"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+CMSClassUnloadingEnabled"
export CATALINA_OPTS="$CATALINA_OPTS -XX:InitiatingHeapOccupancyPercent=70"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UnlockDiagnosticVMOptions"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+UseConcMarkSweepGC"
export CATALINA_OPTS="$CATALINA_OPTS -Xms512m"
export CATALINA_OPTS="$CATALINA_OPTS -Xmx8192m"
export CATALINA_OPTS="$CATALINA_OPTS -XX:MaxMetaspaceSize=256M"

3.service tomcat restart

Beachten Sie, dass die JVM mehr Speicher als nur den Heap verwendet. Beispielsweise werden Java-Methoden, Thread-Stacks und native Handles im vom Heap getrennten Speicher sowie interne JVM-Datenstrukturen zugewiesen.

Musa
quelle
7

Ich habe das gleiche Problem mit Java-Heap-Größe konfrontiert.

Ich habe zwei Lösungen, wenn Sie Java 5 (1.5) verwenden.

  1. Installieren Sie einfach jdk1.6 und gehen Sie zu den Einstellungen von eclipse und legen Sie den jre-Pfad von jav1 1.6 fest, wie Sie installiert haben.

  2. Überprüfen Sie Ihr VM-Argument und lassen Sie es sein, was auch immer es ist. Fügen Sie einfach eine Zeile unter allen in VM-Argumenten vorhandenen Argumenten als -Xms512m -Xmx512m -XX hinzu: MaxPermSize = ... m (192m).

Ich denke es wird funktionieren ...

Soumya Sandeep Mohanty
quelle
7

Wenn Sie Ihre Speichernutzung zur Laufzeit überwachen müssen, java.lang.managementbietet das Paket die Möglichkeit MBeans, die Speicherpools in Ihrer VM (z. B. Eden Space, Tenured Generation usw.) sowie das Garbage Collection-Verhalten zu überwachen.

Der von diesen MBeans gemeldete freie Heap-Speicherplatz hängt stark vom GC-Verhalten ab, insbesondere wenn Ihre Anwendung viele Objekte generiert, die später GC-bearbeitet werden. Ein möglicher Ansatz besteht darin, den freien Heapspeicher nach jedem vollständigen GC zu überwachen, anhand dessen Sie möglicherweise eine Entscheidung über die Freigabe von Speicher durch persistierende Objekte treffen können.

Letztendlich ist es am besten, die Speichererhaltung so weit wie möglich zu begrenzen, während die Leistung akzeptabel bleibt. Wie bereits in einem früheren Kommentar erwähnt, ist der Speicher immer begrenzt, aber Ihre App sollte eine Strategie für den Umgang mit Speichererschöpfung haben.

Leigh
quelle
5

Beachten Sie, dass Sie, wenn Sie dies in einer Bereitstellungssituation benötigen, die Verwendung von Java WebStart in Betracht ziehen (mit einer "Ondisk" -Version, nicht der Netzwerkversion - möglich in Java 6u10 und höher), da Sie damit die verschiedenen Argumente für die JVM in einem Kreuz angeben können Plattform Weg.

Andernfalls benötigen Sie einen betriebssystemspezifischen Starter, der die benötigten Argumente festlegt.

Thorbjørn Ravn Andersen
quelle
Java WebStart wird auslaufen. Mir ist noch kein geeigneter Ersatz bekannt.
Thorbjørn Ravn Andersen
1

Wenn dieses Problem in Wildfly 8 und JDK1.8 auftritt, müssen Sie die MaxMetaSpace-Einstellungen anstelle der PermGen-Einstellungen angeben.

Zum Beispiel müssen wir die folgende Konfiguration in die Datei setenv.sh von wildfly einfügen. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Weitere Informationen finden Sie unter Wildfly Heap Issue

satish
quelle
1

In Bezug auf Netbeans können Sie die maximale Heap-Größe festlegen, um das Problem zu lösen.

Gehen Sie zu 'Ausführen' und dann -> 'Projektkonfiguration festlegen' -> 'Anpassen' -> 'Ausführen' des angezeigten Fensters -> 'VM-Option' -> Geben Sie '-Xms2048m -Xmx2048m' ein. .

Xiaogang
quelle
1

Wenn Sie weiterhin Verweise auf Objekte zuweisen und behalten, füllen Sie jede Menge Speicher auf, die Sie haben.

Eine Möglichkeit besteht darin, eine transparente Datei zu schließen und zu öffnen, wenn die Registerkarten gewechselt werden (Sie behalten nur einen Zeiger auf die Datei, und wenn der Benutzer die Registerkarte wechselt, schließen und bereinigen Sie alle Objekte ... wodurch sich die Datei langsamer ändert ... aber ...) und vielleicht nur 3 oder 4 Dateien im Speicher behalten.

Wenn der Benutzer eine Datei öffnet, lädt und einen OutOfMemoryError abfängt, sollten Sie diese Datei schließen (da das Öffnen der Datei nicht möglich ist), ihre Objekte bereinigen und den Benutzer warnen, dass er nicht verwendete Dateien schließen soll Dateien.

Ihre Idee, den virtuellen Speicher dynamisch zu erweitern, löst das Problem nicht, da die Ressourcen der Maschine begrenzt sind. Sie sollten daher vorsichtig sein und Speicherprobleme behandeln (oder zumindest vorsichtig mit ihnen umgehen).

Ein paar Hinweise, die ich bei Speicherlecks gesehen habe, sind:

-> Denken Sie daran, dass Sie, wenn Sie etwas in eine Sammlung einfügen und es anschließend vergessen, immer noch einen starken Bezug dazu haben. Machen Sie die Sammlung also ungültig, bereinigen Sie sie oder tun Sie etwas damit ... wenn nicht, finden Sie eine Speicherverlust schwer zu finden.

-> Vielleicht kann die Verwendung von Sammlungen mit schwachen Referenzen (schwaches Hashmap ...) bei Speicherproblemen helfen, aber Sie müssen vorsichtig damit sein, da Sie möglicherweise feststellen, dass das gesuchte Objekt gesammelt wurde.

-> Eine andere Idee, die ich gefunden habe, ist die Entwicklung einer dauerhaften Sammlung, die auf Datenbankobjekten gespeichert ist, die am wenigsten verwendet und transparent geladen werden. Dies wäre wahrscheinlich der beste Ansatz ...

SoulWanderer
quelle
0

Wenn alles andere fehlschlägt, erhöhen Sie neben der Erhöhung der maximalen Heap-Größe auch die Swap-Größe. Für Linux finden Sie ab sofort relevante Anweisungen unter https://linuxize.com/post/create-a-linux-swap-file/ .

Dies kann hilfreich sein, wenn Sie z. B. etwas Großes in einer eingebetteten Plattform kompilieren.

nccc
quelle