Come gestire l'errore "java.lang.OutOfMemoryError: Java heap space"?


416

Sto scrivendo un'applicazione Swing sul lato client (designer grafico di caratteri) su Java 5 . Di recente, sto riscontrando un java.lang.OutOfMemoryError: Java heap spaceerrore perché non sono conservatore sull'uso della memoria. L'utente può aprire un numero illimitato di file e il programma mantiene gli oggetti aperti in memoria. Dopo una rapida ricerca, ho trovato Ergonomia nella Java Virtual Machine 5.0 e altri che dicevano su una macchina Windows la dimensione massima dell'heap di default di JVM 64MB.

Data questa situazione, come dovrei affrontare questo vincolo?

Potrei aumentare la dimensione massima dell'heap usando l' opzione della riga di comando su java, ma ciò richiederebbe capire la RAM disponibile e scrivere qualche programma o script di avvio. Inoltre, aumentando fino ad un certo limite finito , alla fine non si sbarazza del problema.

Potrei riscrivere parte del mio codice per rendere persistenti gli oggetti nel file system frequentemente (usare il database è la stessa cosa) per liberare memoria. Potrebbe funzionare, ma probabilmente è anche molto lavoro.

Se potessi indicarmi i dettagli delle idee precedenti o alcune alternative come la memoria virtuale automatica, estendere le dimensioni dell'heap in modo dinamico , sarebbe fantastico.


La dimensione heap massima predefinita di 64 MB è precedente a J2SE 5.0. Per informazioni su J2SE 8.0, consultare "Ergonomia del Garbage Collector" all'indirizzo docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas,

Se sei arrivato qui perché tutte le domande OOM sono state ingannate a questa, assicurati di controllare anche: stackoverflow.com/questions/299659/… Fornisce la soluzione per ripulire i riferimenti di memoria 'just in time' prima dell'OOM. SoftReferences può essere lo strumento che risolve il problema reale.
Steve Steiner,

Risposte:


244

Alla fine hai sempre un limite massimo di heap da utilizzare, indipendentemente dalla piattaforma su cui stai girando. In Windows a 32 bit questo è disponibile 2GB(non specificamente heap ma quantità totale di memoria per processo). Accade semplicemente che Java scelga di ridurre le impostazioni predefinite (presumibilmente in modo che il programmatore non possa creare programmi che hanno allocazione di memoria in fuga senza incorrere in questo problema e dover esaminare esattamente cosa stanno facendo).

Quindi, dato questo, ci sono diversi approcci che potresti adottare per determinare la quantità di memoria necessaria o per ridurre la quantità di memoria che stai utilizzando. Un errore comune con i linguaggi di immondizia raccolti come Java o C # è quello di mantenere i riferimenti agli oggetti che non vengono più utilizzati o di allocare molti oggetti quando è possibile riutilizzarli . Finché gli oggetti hanno un riferimento ad essi, continueranno a utilizzare lo spazio heap poiché il Garbage Collector non li eliminerà.

In questo caso è possibile utilizzare un profiler di memoria Java per determinare quali metodi nel programma stanno allocando un numero elevato di oggetti e quindi determinare se esiste un modo per assicurarsi che non siano più referenziati o per non allocarli in primo luogo. Un'opzione che ho usato in passato è "JMP" http://www.khelekore.org/jmp/ .

Se si determina che si stanno allocando questi oggetti per un motivo e è necessario mantenere i riferimenti (a seconda di ciò che si sta facendo, potrebbe essere il caso), sarà sufficiente aumentare la dimensione heap massima all'avvio del programma. Tuttavia, una volta eseguita la profilazione della memoria e compreso come vengono allocati i tuoi oggetti, dovresti avere un'idea migliore di quanta memoria hai bisogno.

In generale, se non è possibile garantire che il programma verrà eseguito in una quantità limitata di memoria (forse in base alla dimensione dell'input), si verificherà sempre questo problema. Solo dopo aver esaurito tutto ciò, dovrai cercare nella cache gli oggetti sul disco, ecc. A questo punto dovresti avere un'ottima ragione per dire "Ho bisogno di Xgb di memoria" per qualcosa e non puoi aggirare migliorando i tuoi algoritmi o schemi di allocazione della memoria. In genere questo sarà generalmente il caso degli algoritmi che operano su set di dati di grandi dimensioni (come un database o alcuni programmi di analisi scientifica) e quindi tecniche come la cache e l'IO mappato in memoria diventano utili.


6
OpenJDK e OracleJDK hanno raggruppato il profiler - jvisualvm. Se desideri maggiori comodità, ti suggerisco Yourkit commerciale.
Petr Gladkikh,

121

Esegui Java con l'opzione della riga di comando -Xmx, che imposta la dimensione massima dell'heap.

Vedi qui per i dettagli .


3
Come impostare questo parametro per sempre? Perché sto usando il comando 'gradlew assemble'.
Dr.jacky,

2
Esegui-> Esegui configurazioni-> Fai clic sugli argomenti-> all'interno degli argomenti della VM digita -Xms1g -Xmx2g
Arayan Singh,

2
Questa è la vera risposta.
nccc,

85

È possibile specificare per progetto la quantità di spazio heap richiesta dal progetto

Quello che segue è per Eclipse Helios / Juno / Kepler :

Fare clic con il tasto destro del mouse su

 Run As - Run Configuration - Arguments - Vm Arguments, 

quindi aggiungi questo

-Xmx2048m

1
ciao bighostkim e cuongHuyTo, dove si trova "Argomenti" .. posso vedere fino a Esegui configurazione. Per favore, tel. Ho bisogno di scaricare e archiviare quasi 2000 contatti da Gmail. Si è arrestato in modo anomalo a causa di un'eccezione di memoria
insufficiente

@AndroiRaji: fai clic con il pulsante destro del mouse sulla classe Java che ha un valore principale eseguibile (ovvero "null statico pubblico (String [] args)"), quindi scegli Esegui come - Esegui configurazione. Quindi "Argomenti" è la scheda subito dopo il Principale (vedi le schede Principale, Argomenti, JRE, Percorso di classe, Sorgente, Ambiente, Comune).
CuongHuyTo

47

Aumentare la dimensione dell'heap non è una "correzione", è un "cerotto", temporaneo al 100%. Si schianterà di nuovo da qualche altra parte. Per evitare questi problemi, scrivere codice ad alte prestazioni.

  1. Usa le variabili locali ove possibile.
  2. Assicurati di selezionare l'oggetto corretto (EX: selezione tra String, StringBuffer e StringBuilder)
  3. Utilizzare un buon sistema di codice per il proprio programma (EX: utilizzo di variabili statiche VS variabili non statiche)
  4. Altre cose che potrebbero funzionare sul tuo codice.
  5. Prova a muoverti con multy THREADING

Questo è così vero Sto cercando di risolvere un problema in cui sto ricevendo OOM sul thread AWT ma se uso un nuovo thread diverso, non ottengo il problema OOM. Tutto quello che posso trovare online è aumentare la dimensione dell'heap per il thread AWT.
Ashish,

@Ash: Sì, risolvi il problema principale invece di cercare cerotti.
Succo di limone,

La garbage collection e l'approccio di gestione della memoria in Java avrebbero dovuto risolvere tutte queste complicazioni malloc-dealloc dei suoi predecessori :( Naturalmente sono completamente d'accordo con questa risposta è solo un peccato che le impostazioni predefinite non rendano facile scrivere codice con dati snelli -strutture che vengono ripulite al più presto.
Davos,

31

Un grande avvertimento: nel mio ufficio, stavamo scoprendo che (su alcune macchine Windows) non potevamo assegnare più di 512 m per l'heap Java. Ciò si è rivelato dovuto al prodotto antivirus Kaspersky installato su alcuni di questi computer. Dopo aver disinstallato quel prodotto AV, abbiamo scoperto che potremmo allocare almeno 1,6 gb, ovvero -Xmx1600m(m è obbligatorio in altro modo che porterà a un altro errore "Heap iniziale troppo piccolo") funziona.

Non ho idea se ciò accada con altri prodotti AV, ma presumibilmente ciò accade perché il programma AV sta riservando un piccolo blocco di memoria in ogni spazio degli indirizzi, impedendo così un'unica allocazione davvero grande.


22

Gli argomenti VM hanno funzionato per me in Eclipse. Se si utilizza eclipse versione 3.4, procedere come segue

vai Run --> Run Configurations -->quindi seleziona il progetto in maven build -> quindi seleziona la scheda "JRE" -> quindi inserisci-Xmx1024m .

In alternativa, puoi fare Run --> Run Configurations --> select the "JRE" tab -->quindi inserire -Xmx1024m

Ciò dovrebbe aumentare l'heap di memoria per tutte le build / i progetti. La dimensione della memoria sopra è di 1 GB. Puoi ottimizzare nel modo desiderato.


18

Sì, con -Xmxte puoi configurare più memoria per la tua JVM. Per essere sicuri di non perdere o sprecare memoria. Prendi un dump dell'heap e usa Eclipse Memory Analyzer per analizzare il consumo di memoria.


JVMJ9VM007E Opzione della riga di comando non riconosciuta: -Xmx Impossibile creare la macchina virtuale Java. Downvote
Philip Rego,

17

Vorrei aggiungere raccomandazioni per la risoluzione dei problemi dell'oracolo .

Eccezione nel thread thread_name: java.lang.OutOfMemoryError: spazio heap Java

Il messaggio di dettaglio Spazio heap Java indica che non è stato possibile assegnare l'oggetto nell'heap Java. Questo errore non implica necessariamente una perdita di memoria

Cause possibili:

  1. Problema di configurazione semplice , in cui la dimensione heap specificata non è sufficiente per l'applicazione.

  2. L'applicazione contiene involontariamente riferimenti a oggetti e ciò impedisce che gli oggetti vengano raccolti in modo inutile.

  3. Uso eccessivo di finalizzatori .

Un'altra potenziale fonte di questo errore si presenta con applicazioni che fanno un uso eccessivo di finalizzatori. Se una classe ha un metodo finalize, gli oggetti di quel tipo non avranno il loro spazio recuperato al momento della garbage collection

Dopo la garbage collection , gli oggetti vengono messi in coda per la finalizzazione , che si verifica in un secondo momento. i finalizzatori sono eseguiti da un thread demone che serve la coda di finalizzazione. Se il thread del finalizzatore non riesce a tenere il passo con la coda di finalizzazione, l'heap Java potrebbe riempire e questo tipo di OutOfMemoryError eccezione verrebbe generato.

Uno scenario che può causare questa situazione è quando un'applicazione crea thread ad alta priorità che causano un aumento della coda di finalizzazione a una velocità superiore alla velocità con cui il thread del finalizzatore gestisce tale coda.


9

Seguire i passaggi seguenti:

  1. Apri catalina.shda Tomcat / Bin.

  2. Cambia 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. Riavvia Tomcat


8

Ho letto da qualche altra parte che puoi provare: cattura java.lang.OutOfMemoryError e sul blocco catch, puoi liberare tutte le risorse che conosci potrebbero usare molta memoria, chiudere le connessioni e così via, quindi System.gc()provare a provare di nuovo stavi per fare.

Un altro modo è questo, anche se non so se funzionerebbe, ma attualmente sto testando se funzionerà sulla mia applicazione.

L'idea è di fare Garbage Collection chiamando System.gc () che è noto per aumentare la memoria libera. È possibile continuare a controllare questo dopo che viene eseguito un codice di assorbimento della memoria.

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

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();

6
In generale, penso che la JVM preferirà Garbage Collect (GC) piuttosto che lanciare un OutOfMemoryError. Chiamare esplicitamente System.gc () dopo OutOfMemoryError potrebbe eventualmente aiutare su alcune VM / configurazioni, ma non mi aspetto che funzioni molto bene nel caso generale. Tuttavia, l'eliminazione di riferimenti a oggetti non necessari sarebbe sicuramente utile in quasi tutti i casi.
Mike Clark,

6
@mwangi Chiamare System.gc () direttamente dal codice è generalmente una cattiva idea. È solo un suggerimento per JVM che GC dovrebbe essere eseguito, ma non c'è assolutamente alcuna garanzia che verrà eseguito.

7

Un modo semplice per risolvere OutOfMemoryErrorin java è aumentare la dimensione massima dell'heap utilizzando le opzioni JVM-Xmx512M , questo risolverà immediatamente il tuo OutOfMemoryError. Questa è la mia soluzione preferita quando ottengo OutOfMemoryError in Eclipse, Maven o ANT durante la costruzione del progetto perché in base alle dimensioni del progetto si può facilmente esaurire la memoria.

Ecco un esempio di aumento della dimensione massima dell'heap di JVM, inoltre è meglio mantenere la razione da -Xmx a -Xms 1: 1 o 1: 1.5 se si sta impostando la dimensione dell'heap nell'applicazione java.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Link di riferimento


1
Qualche idea sul perché dobbiamo tenerli nel rapporto 1: 1 o 1: 1.5?
Ernesto,

7

Per impostazione predefinita, JVM utilizza dimensioni ridotte e configurazione ridotta per altre funzionalità relative alle prestazioni. Ma per la produzione è possibile ottimizzare ad es. (Inoltre può esistere una configurazione specifica di Application Server) -> (Se la memoria non è ancora sufficiente per soddisfare la richiesta e l'heap ha già raggiunto la dimensione massima, si verificherà un OutOfMemoryError)

-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)

Ad esempio: sulla piattaforma Linux per le impostazioni preferibili della modalità di produzione.

Dopo aver scaricato e configurato il server in questo modo http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1.creare il file setenv.sh nella cartella / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2.Aprire e scrivere questo parametro per impostare la modalità preferibile.

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

Si noti che JVM utilizza più memoria rispetto al solo heap. Ad esempio metodi Java, stack di thread e handle nativi sono allocati in memoria separatamente dall'heap, nonché dalle strutture di dati interne JVM.


7

Ho riscontrato lo stesso problema dalle dimensioni dell'heap java.

Ho due soluzioni se stai usando Java 5 (1.5).

  1. basta installare jdk1.6 e andare alle preferenze di eclipse e impostare il percorso jre di jav1 1.6 come è stato installato.

  2. Controlla il tuo argomento VM e lascia che sia quello che è. basta aggiungere una riga sotto di tutti gli argomenti presenti negli argomenti VM come -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Penso che funzionerà...


7

Se è necessario monitorare l'utilizzo della memoria in fase di esecuzione, le java.lang.managementofferte del pacchetto MBeansche possono essere utilizzate per monitorare i pool di memoria nella macchina virtuale (ad es. Spazio eden, generazione in possesso, ecc.) E anche il comportamento della garbage collection.

Lo spazio di heap libero riportato da questi MBean varierà notevolmente a seconda del comportamento del GC, in particolare se l'applicazione genera molti oggetti che sono successivamente GC-ed. Un possibile approccio consiste nel monitorare lo spazio di heap libero dopo ogni GC completo, che potrebbe essere possibile utilizzare per prendere una decisione sulla liberazione della memoria mediante la persistenza di oggetti.

In definitiva, la soluzione migliore è limitare la conservazione della memoria il più possibile mentre le prestazioni rimangono accettabili. Come osservato in precedenza, la memoria è sempre limitata, ma l'app dovrebbe avere una strategia per gestire l'esaurimento della memoria.


5

Si noti che se è necessario questo in una situazione di distribuzione, considerare l'utilizzo di Java WebStart (con una versione "ondisk", non quella di rete - possibile in Java 6u10 e versioni successive) in quanto consente di specificare i vari argomenti per la JVM in una croce modo piattaforma.

Altrimenti avrai bisogno di un launcher specifico del sistema operativo che imposta gli argomenti di cui hai bisogno.


Java WebStart è in fase di eliminazione. Non sono ancora a conoscenza di una sostituzione adeguata.
Thorbjørn Ravn Andersen,

1

Se questo problema si verifica in Wildfly 8 e JDK1.8, è necessario specificare le impostazioni MaxMetaSpace anziché le impostazioni PermGen.

Ad esempio, dobbiamo aggiungere la configurazione seguente nel file setenv.sh di wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Per ulteriori informazioni, consultare il problema dell'heap Wildfly


1

Per quanto riguarda i netbeans, è possibile impostare la dimensione massima dell'heap per risolvere il problema.

Vai su "Esegui", quindi -> "Imposta configurazione progetto" -> "Personalizza" -> "esegui" della finestra visualizzata -> "Opzione VM" -> compila "-Xms2048m -Xmx2048m" .


1

Se continui ad allocare e mantenere i riferimenti all'oggetto, riempirai qualsiasi quantità di memoria che hai.

Un'opzione è chiudere e aprire un file trasparente quando cambiano le schede (mantieni solo un puntatore al file e quando l'utente cambia scheda, chiudi e pulisci tutti gli oggetti ... renderà più lento il cambio del file ... ma ...) e forse conserva solo 3 o 4 file in memoria.

Un'altra cosa che dovresti fare è, quando l'utente apre un file, caricarlo e intercettare qualsiasi OutOfMemoryError, quindi (poiché non è possibile aprire il file) chiudere quel file, pulire i suoi oggetti e avvisare l'utente che dovrebbe chiudere inutilizzato File.

L'idea di estendere dinamicamente la memoria virtuale non risolve il problema, poiché la macchina ha risorse limitate, quindi è necessario prestare attenzione e gestire i problemi di memoria (o almeno, fare attenzione con essi).

Un paio di suggerimenti che ho visto con perdite di memoria sono:

-> Tieni presente che se metti qualcosa in una collezione e poi ti dimentichi di essa, hai ancora un forte riferimento ad essa, quindi annulla la collezione, puliscila o fai qualcosa con essa ... se non troverai un perdita di memoria difficile da trovare.

-> Forse, l'utilizzo di raccolte con riferimenti deboli (weakhashmap ...) può aiutare con problemi di memoria, ma è necessario stare attento con esso, perché potresti scoprire che l'oggetto che stai cercando è stato raccolto.

-> Un'altra idea che ho trovato è quella di sviluppare una collezione persistente che è memorizzata su oggetti di database meno utilizzati e caricati in modo trasparente. Questo sarebbe probabilmente l'approccio migliore ...


0

Se tutto il resto fallisce, oltre ad aumentare la dimensione massima dell'heap, prova anche ad aumentare la dimensione dello scambio. Per Linux, a partire da ora, le istruzioni pertinenti sono disponibili in https://linuxize.com/post/create-a-linux-swap-file/ .

Questo può aiutare se, ad esempio, stai compilando qualcosa di grosso in una piattaforma integrata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.