¿Cómo lidiar con el error "java.lang.OutOfMemoryError: Java heap space"?

416

Estoy escribiendo una aplicación Swing del lado del cliente (diseñador gráfico de fuentes) en Java 5 . Recientemente, me encuentro con un java.lang.OutOfMemoryError: Java heap spaceerror porque no estoy siendo conservador en el uso de la memoria. El usuario puede abrir un número ilimitado de archivos, y el programa mantiene los objetos abiertos en la memoria. Después de una investigación rápida, encontré Ergonomics en la máquina virtual Java 5.0 y otros que dicen en la máquina Windows que el JVM tiene el tamaño máximo de almacenamiento dinámico predeterminado 64MB.

Dada esta situación, ¿cómo debo lidiar con esta restricción?

Podría aumentar el tamaño máximo de almacenamiento dinámico utilizando la opción de línea de comandos para Java, pero eso requeriría averiguar la RAM disponible y escribir algún programa o script de inicio. Además, aumentar a un máximo finito no elimina el problema en última instancia .

Podría reescribir parte de mi código para persistir objetos en el sistema de archivos con frecuencia (usar la base de datos es lo mismo) para liberar la memoria. Podría funcionar, pero probablemente también sea mucho trabajo.

Si pudiera señalarme los detalles de las ideas anteriores o algunas alternativas como la memoria virtual automática, extender el tamaño del montón dinámicamente , sería genial.

Eugene Yokota
fuente
El tamaño de almacenamiento dinámico máximo predeterminado de 64 MB es anterior a J2SE 5.0. Para obtener información sobre J2SE 8.0, consulte "Ergonomía del recolector de basura" en docs.oracle.com/javase/8/docs/technotes/guides/vm/… .
Andy Thomas
Si aterrizó aquí porque todas las preguntas de OOM están engañadas a esta, asegúrese de consultar también: stackoverflow.com/questions/299659/… Proporciona la solución para limpiar las referencias de memoria 'justo a tiempo' antes del OOM. SoftReferences puede ser la herramienta que resuelve su problema real.
Steve Steiner

Respuestas:

244

En última instancia, siempre tiene un máximo de almacenamiento dinámico finito para usar, sin importar en qué plataforma se esté ejecutando. En Windows de 32 bits, esto es alrededor 2GB(no específicamente el montón sino la cantidad total de memoria por proceso). Simplemente sucede que Java elige hacer que el valor predeterminado sea más pequeño (presumiblemente para que el programador no pueda crear programas que tengan una asignación de memoria desbocada sin encontrarse con este problema y tener que examinar exactamente lo que están haciendo).

Por lo tanto, dado que existen varios enfoques que puede tomar para determinar qué cantidad de memoria necesita o para reducir la cantidad de memoria que está utilizando. Un error común con los lenguajes recolectados de basura como Java o C # es mantener referencias a objetos que ya no está utilizando, o asignar muchos objetos cuando podría reutilizarlos . Mientras los objetos tengan una referencia a ellos, continuarán utilizando el espacio de almacenamiento dinámico ya que el recolector de basura no los eliminará.

En este caso, puede usar un generador de perfiles de memoria Java para determinar qué métodos en su programa están asignando una gran cantidad de objetos y luego determinar si hay una manera de asegurarse de que ya no se haga referencia a ellos, o no asignarlos en primer lugar. Una opción que he usado en el pasado es "JMP" http://www.khelekore.org/jmp/ .

Si determina que está asignando estos objetos por un motivo y necesita mantener referencias (dependiendo de lo que esté haciendo, este podría ser el caso), solo necesitará aumentar el tamaño máximo de almacenamiento dinámico cuando inicie el programa. Sin embargo, una vez que realice el perfil de la memoria y comprenda cómo se asignan sus objetos, debería tener una mejor idea de cuánta memoria necesita.

En general, si no puede garantizar que su programa se ejecutará en una cantidad finita de memoria (tal vez dependiendo del tamaño de entrada), siempre se encontrará con este problema. Solo después de agotar todo esto, tendrá que buscar objetos de almacenamiento en caché en el disco, etc. En este punto, debe tener una muy buena razón para decir "Necesito Xgb de memoria" para algo y no puede solucionarlo mejorando sus algoritmos o patrones de asignación de memoria. En general, este solo será el caso de los algoritmos que operan en grandes conjuntos de datos (como una base de datos o algún programa de análisis científico) y luego las técnicas como el almacenamiento en caché y el IO mapeado en memoria se vuelven útiles.

Ben Childs
fuente
66
OpenJDK y OracleJDK han incluido el generador de perfiles - jvisualvm. Si desea más comodidades, le sugiero Yourkit comercial.
Petr Gladkikh
121

Ejecute Java con la opción de línea de comandos -Xmx, que establece el tamaño máximo del montón.

Ver aquí para más detalles .

Dave Webb
fuente
3
¿Cómo configurar este parámetro para siempre? Porque estoy usando el comando 'gradlew assemble'.
Dr.jacky
2
Ejecutar-> Ejecutar configuraciones-> Haga clic en argumentos-> dentro de los argumentos de VM escriba -Xms1g -Xmx2g
Arayan Singh
2
Esa es la verdadera respuesta.
nccc
85

Puede especificar por proyecto cuánto espacio de almacenamiento dinámico desea su proyecto

Lo siguiente es para Eclipse Helios / Juno / Kepler :

Clic derecho del mouse en

 Run As - Run Configuration - Arguments - Vm Arguments, 

luego agrega esto

-Xmx2048m
allenhwkim
fuente
1
hola bighostkim y cuongHuyTo, donde está "Argumentos" ... puedo ver hasta Ejecutar configuración. Por favor dime. Mi necesidad de descargar y almacenar casi 2000 contactos de gmail. Se
bloquea
@AndroiRaji: hace clic con el botón derecho del mouse en la clase Java que tiene un main ejecutable (que es "public static void main (String [] args)"), luego elige Ejecutar como - Ejecutar configuración. Entonces "Argumentos" es la pestaña justo después de la Principal (verá las pestañas Principal, Argumentos, JRE, Classpath, Fuente, Medio Ambiente, Común).
CuongHuyTo
47

Aumentar el tamaño del montón no es una "solución", es un "yeso", 100% temporal. Se bloqueará nuevamente en otro lugar. Para evitar estos problemas, escriba código de alto rendimiento.

  1. Use variables locales siempre que sea posible.
  2. Asegúrese de seleccionar el objeto correcto (EX: Selección entre String, StringBuffer y StringBuilder)
  3. Use un buen sistema de código para su programa (EX: Uso de variables estáticas VS variables no estáticas)
  4. Otras cosas que podrían funcionar en su código.
  5. Intenta moverte con multy THREADING
Jugo de limon
fuente
Esto es tan cierto. Estoy tratando de solucionar un problema donde obtengo OOM en el hilo AWT pero si uso un hilo nuevo diferente, no obtengo un problema OOM. Todo lo que puedo encontrar en línea es aumentar el tamaño de almacenamiento dinámico para el hilo AWT.
Ashish
@ Ash: Sí, soluciona el problema central en lugar de buscar yesos.
Jugo de limón
Se suponía que la recolección de basura y el enfoque de administración de memoria en Java resolverían todas estas complicaciones malloc-dealloc de sus predecesores :( Por supuesto, estoy completamente de acuerdo con esta respuesta, es una pena que los valores predeterminados no faciliten la escritura de código con datos lean -Estructuras que se limpian lo antes posible.
Davos
31

Gran advertencia ---- en mi oficina, estábamos descubriendo que (en algunas máquinas con Windows) no podíamos asignar más de 512 m para el almacenamiento dinámico de Java. Esto se debió al producto antivirus Kaspersky instalado en algunas de esas máquinas. Después de desinstalar ese producto AV, descubrimos que podíamos asignar al menos 1,6 gb, es decir, -Xmx1600m (m es obligatorio de otra manera, conducirá a otro error "Montón inicial demasiado pequeño").

No tengo idea de si esto sucede con otros productos AV, pero presumiblemente esto está sucediendo porque el programa AV está reservando un pequeño bloque de memoria en cada espacio de direcciones, evitando así una sola asignación realmente grande.

David
fuente
22

Los argumentos de VM funcionaron para mí en eclipse. Si está utilizando eclipse versión 3.4, haga lo siguiente

vaya a Run --> Run Configurations -->luego seleccione el proyecto en Maven build -> luego seleccione la pestaña "JRE" -> luego ingrese -Xmx1024m.

Alternativamente, puede hacerlo y Run --> Run Configurations --> select the "JRE" tab -->luego ingresar -Xmx1024m

Esto debería aumentar el montón de memoria para todas las compilaciones / proyectos. El tamaño de memoria anterior es de 1 GB. Puede optimizar la forma que desee.

amar a todo
fuente
18

Sí, con -Xmxusted puede configurar más memoria para su JVM. Para asegurarse de que no pierda ni pierda memoria. Realice un volcado de almacenamiento dinámico y use el Analizador de memoria Eclipse para analizar el consumo de memoria.

kohlerm
fuente
JVMJ9VM007E Opción de línea de comandos no reconocida: -Xmx No se pudo crear la máquina virtual Java. Voto a favor
Philip Rego
17

Me gustaría agregar recomendaciones de solución de problemas de Oracle artículo .

Excepción en el hilo thread_name: java.lang.OutOfMemoryError: espacio de almacenamiento dinámico de Java

El mensaje detallado El espacio de almacenamiento dinámico de Java indica que el objeto no se pudo asignar en el almacenamiento dinámico de Java. Este error no implica necesariamente una pérdida de memoria

Posibles Causas:

  1. Problema de configuración simple , donde el tamaño de almacenamiento dinámico especificado es insuficiente para la aplicación.

  2. La aplicación contiene referencias involuntarias a objetos , y esto evita que los objetos se recojan basura.

  3. Uso excesivo de finalizadores .

Otra fuente potencial de este error surge con las aplicaciones que hacen un uso excesivo de los finalizadores. Si una clase tiene un método de finalización, entonces los objetos de ese tipo no tienen su espacio reclamado en el momento de la recolección de basura

Después de la recolección de basura , los objetos se ponen en cola para su finalización , lo que ocurre más adelante. Los finalizadores son ejecutados por un hilo de daemon que da servicio a la cola de finalización. Si el subproceso finalizador no puede seguir el ritmo de la cola de finalización, entonces el montón de Java podría llenarse y se generaría este tipo de excepción OutOfMemoryError .

Un escenario que puede causar esta situación es cuando una aplicación crea subprocesos de alta prioridad que hacen que la cola de finalización aumente a una velocidad que es más rápida que la velocidad a la que el subproceso finalizador está atendiendo esa cola.

Ravindra babu
fuente
9

Siga los pasos a continuación:

  1. Abierto catalina.shdesde tomcat / bin.

  2. Cambiar JAVA_OPTS a

    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. Reinicia tu gato

Pradip Bhatt
fuente
8

Leí en otro lugar que puedes probar: catch java.lang.OutOfMemoryError y en el bloque catch, puedes liberar todos los recursos que sabes que pueden usar mucha memoria, cerrar conexiones, etc., y luego hacer un System.gc() luego volver a intentar lo que sea que ibas a hacer

Otra forma es esta, aunque no sé si esto funcionaría, pero actualmente estoy probando si funcionará en mi aplicación.

La idea es hacer la recolección de basura llamando a System.gc () que se sabe que aumenta la memoria libre. Puede seguir verificando esto después de que se ejecute un código de memoria.

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

Runtime runtime = Runtime.getRuntime();

if(runtime.freeMemory()<minRunningMemory)
 System.gc();
mwangi
fuente
66
En general, creo que la JVM preferirá Garbage Collect (GC) en lugar de lanzar un OutOfMemoryError. Llamar explícitamente a System.gc () después de OutOfMemoryError podría ayudar en algunas VM / configuraciones, pero no esperaría que funcionara muy bien en el caso general. Sin embargo, descartar referencias innecesarias de objetos definitivamente ayudaría en casi todos los casos.
Mike Clark el
66
@mwangi Calling System.gc () directamente desde el código generalmente es una mala idea. Es solo una sugerencia a JVM que se debe realizar GC, pero no hay absolutamente ninguna garantía de que se realice.
7

Una manera fácil de resolver OutOfMemoryErroren Java es aumentar el tamaño máximo de almacenamiento dinámico utilizando las opciones de JVM-Xmx512M almacenamiento , esto resolverá inmediatamente su OutOfMemoryError. Esta es mi solución preferida cuando obtengo OutOfMemoryError en Eclipse, Maven o ANT mientras construyo el proyecto porque, según el tamaño del proyecto, puede quedarse sin memoria fácilmente.

Aquí hay un ejemplo de cómo aumentar el tamaño máximo de almacenamiento dinámico de JVM. También es mejor mantener la ración -Xmx a -Xms 1: 1 o 1: 1.5 si está configurando el tamaño de almacenamiento dinámico en su aplicación Java.

export JVM_ARGS="-Xms1024m -Xmx1024m"

Link de referencia

Chaukssey
fuente
1
¿Alguna idea de por qué debemos mantenerlos en una proporción de 1: 1 o 1: 1.5?
ernesto
7

De forma predeterminada para el desarrollo, JVM utiliza un tamaño pequeño y una configuración pequeña para otras características relacionadas con el rendimiento. Pero para la producción puede ajustar, por ejemplo, (además, puede existir una configuración específica del servidor de aplicaciones) -> (Si todavía no hay suficiente memoria para satisfacer la solicitud y el montón ya ha alcanzado el tamaño máximo, se producirá 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)

Por ejemplo: en la plataforma Linux para la configuración preferible del modo de producción.

Después de descargar y configurar el servidor de esta manera http://www.ehowstuff.com/how-to-install-and-setup-apache-tomcat-8-on-centos-7-1-rhel-7/

1.cree el archivo setenv.sh en la carpeta / opt / tomcat / bin /

   touch /opt/tomcat/bin/setenv.sh

2. Abra y escriba estos parámetros para configurar el modo preferible.

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

Tenga en cuenta que la JVM usa más memoria que solo el montón. Por ejemplo, los métodos Java, las pilas de subprocesos y los identificadores nativos se asignan en memoria separada del montón, así como las estructuras de datos internos de JVM.

Musa
fuente
7

Me he enfrentado al mismo problema con el tamaño del montón de Java.

Tengo dos soluciones si está utilizando Java 5 (1.5).

  1. simplemente instale jdk1.6 y vaya a las preferencias de eclipse y configure la ruta jre de jav1 1.6 tal como la ha instalado.

  2. Verifique su argumento VM y deje que sea lo que sea. simplemente agregue una línea debajo de todos los argumentos presentes en los argumentos de VM como -Xms512m -Xmx512m -XX: MaxPermSize = ... m (192m).

Creo que funcionará...

Soumya Sandeep Mohanty
fuente
7

Si necesita controlar su uso de memoria en tiempo de ejecución, el java.lang.managementpaquete ofreceMBeans que puede usarse para monitorear los grupos de memoria en su VM (por ejemplo, espacio edén, generación permanente, etc.), y también el comportamiento de recolección de basura.

El espacio de almacenamiento dinámico libre reportado por estos MBeans variará enormemente dependiendo del comportamiento de GC, particularmente si su aplicación genera muchos objetos que luego se editan por GC. Un enfoque posible es monitorear el espacio de almacenamiento dinámico libre después de cada GC completo, que puede utilizar para tomar una decisión sobre la liberación de memoria mediante la persistencia de objetos.

En última instancia, su mejor opción es limitar su retención de memoria tanto como sea posible mientras el rendimiento sigue siendo aceptable. Como se señaló en un comentario anterior, la memoria siempre es limitada, pero su aplicación debe tener una estrategia para lidiar con el agotamiento de la memoria.

Leigh
fuente
5

Tenga en cuenta que si necesita esto en una situación de implementación, considere usar Java WebStart (con una versión "ondisk", no la de red, posible en Java 6u10 y posterior), ya que le permite especificar los diversos argumentos para la JVM en forma cruzada camino de plataforma.

De lo contrario, necesitará un iniciador específico del sistema operativo que establezca los argumentos que necesita.

Thorbjørn Ravn Andersen
fuente
Java WebStart se está eliminando gradualmente. Todavía no estoy al tanto de un reemplazo adecuado.
Thorbjørn Ravn Andersen
1

Si este problema ocurre en Wildfly 8 y JDK1.8, entonces debemos especificar la configuración de MaxMetaSpace en lugar de la configuración de PermGen.

Por ejemplo, necesitamos agregar la siguiente configuración en el archivo setenv.sh de wildfly. JAVA_OPTS="$JAVA_OPTS -XX:MaxMetaspaceSize=256M"

Para obtener más información, consulte la publicación Wildfly Heap Issue

satish
fuente
1

Con respecto a los netbeans, puede establecer el tamaño máximo de almacenamiento dinámico para resolver el problema.

Vaya a 'Ejecutar', luego -> 'Establecer configuración del proyecto' -> 'Personalizar' -> 'ejecutar' de su ventana emergente -> 'Opción VM' -> complete '-Xms2048m -Xmx2048m' .

Xiaogang
fuente
1

Si sigue asignando y manteniendo referencias a objetos, completará cualquier cantidad de memoria que tenga.

Una opción es cerrar y abrir un archivo transparente cuando cambian las pestañas (solo mantiene un puntero al archivo, y cuando el usuario cambia la pestaña, cierra y limpia todos los objetos ... hará que el archivo cambie más lentamente ... pero ...), y tal vez mantenga solo 3 o 4 archivos en la memoria.

Otra cosa que debe hacer es, cuando el usuario abre un archivo, lo carga e intercepta cualquier OutOfMemoryError, luego (ya que no es posible abrir el archivo) cierra ese archivo, limpia sus objetos y advierte al usuario que debe cerrar sin usar archivos.

Su idea de ampliar dinámicamente la memoria virtual no resuelve el problema, ya que la máquina tiene recursos limitados, por lo que debe tener cuidado y manejar los problemas de memoria (o al menos, tener cuidado con ellos).

Un par de pistas que he visto con pérdidas de memoria es:

-> Recuerda que si pones algo en una colección y luego te olvidas de él, aún tienes una fuerte referencia, así que anula la colección, límpiala o haz algo con ella ... si no, encontrarás un pérdida de memoria difícil de encontrar.

-> Tal vez, el uso de colecciones con referencias débiles (débileshashmap ...) puede ayudar con problemas de memoria, pero debe tener cuidado con eso, ya que puede encontrar que el objeto que busca se ha recopilado.

-> Otra idea que he encontrado es desarrollar una colección persistente que se almacena en los objetos de la base de datos menos utilizados y cargados de forma transparente. Este sería probablemente el mejor enfoque ...

Portero de almas
fuente
0

Si todo lo demás falla, además de aumentar el tamaño máximo de almacenamiento dinámico, intente también aumentar el tamaño de intercambio. Para Linux, a partir de ahora, las instrucciones relevantes se pueden encontrar en https://linuxize.com/post/create-a-linux-swap-file/ .

Esto puede ayudar, por ejemplo, compilando algo grande en una plataforma integrada.

nccc
fuente