¿Cómo manejar OutOfMemoryError en Java?

Tengo que serializar alrededor de un millón de elementos y obtengo la siguiente excepción cuando ejecuto mi código:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Unknown Source) at java.lang.String.(Unknown Source) at java.io.BufferedReader.readLine(Unknown Source) at java.io.BufferedReader.readLine(Unknown Source) at org.girs.TopicParser.dump(TopicParser.java:23) at org.girs.TopicParser.main(TopicParser.java:59) 

¿Cómo manejo esto?

Idealmente, restructure su código para usar menos memoria. Por ejemplo, quizás podría transmitir la salida en lugar de mantener todo en la memoria.

Alternativamente, solo dale a la JVM más memoria con la opción -Xmx .

Sé que la respuesta oficial de Java es “¡Oh, no! ¡Sin recuerdos! ¡Me rindo!”. Esto es bastante frustrante para cualquiera que haya progtwigdo en entornos en los que quedarse sin memoria no puede ser un error fatal (por ejemplo, escribir un sistema operativo o escribir aplicaciones para sistemas operativos no protegidos).

Es necesario estar dispuesto a rendirse: no puede controlar todos los aspectos de la asignación de memoria de Java, por lo que no puede garantizar que su progtwig tenga éxito en condiciones de poca memoria. Pero eso no significa que debes caer sin luchar.

Sin embargo, antes de luchar, podrías buscar formas de evitar la necesidad. Quizás pueda evitar la serialización de Java y, en su lugar, defina su propio formato de datos que no requiere una asignación de memoria significativa para crear. La serialización asigna mucha memoria porque mantiene un registro de los objetos que ha visto antes, de modo que si vuelven a ocurrir, puede hacer referencia a ellos por número en lugar de volverlos a generar (lo que podría conducir a un ciclo infinito). Pero eso se debe a que debe ser de propósito general: según su estructura de datos, es posible que pueda definir alguna representación de texto / binario / XML / lo que pueda escribirse en una transmisión con muy poca necesidad de almacenar un estado adicional. O bien, puede organizar que cualquier estado adicional que necesite esté almacenado en los objetos todo el tiempo, no creado en el momento de la serialización.

Si su aplicación realiza una operación que usa mucha memoria, pero en su mayoría usa mucho menos, y especialmente si esa operación es iniciada por el usuario, y si no puede encontrar una manera de usar menos memoria o hacer que haya más memoria disponible, entonces podría valer la pena capturar OutOfMemory. Puede recuperar informando al usuario que el problema es demasiado grande e invitándolo a recortarlo e intentarlo de nuevo. Si solo pasaron una hora configurando su problema, no querrás abandonar el progtwig y perder todo: quieres darles la oportunidad de hacer algo al respecto. Siempre que el error esté atrapado en la stack, el exceso de memoria no será referenciado para el momento en que se capture el error, lo que le dará a la máquina virtual al menos la oportunidad de recuperarse. Asegúrese de detectar el error debajo de su código de manejo de eventos habitual (capturar OutOfMemory en el manejo regular de eventos puede resultar en bucles muy ocupados, porque intenta mostrar un diálogo al usuario, todavía está sin memoria y detecta otro error). ) Captúrelo solo alrededor de la operación que ha identificado como el cerdito de la memoria, para que los OutOfMemoryErrors que no puede manejar, que provienen de un código que no sea el de la memoria, no se atrapen.

Incluso en una aplicación no interactiva, puede tener sentido abandonar la operación fallida, pero para que el progtwig siga funcionando, procesando más datos. Esta es la razón por la cual los servidores web administran múltiples procesos de manera que si una solicitud de página falla por falta de memoria, el servidor no se cae. Como dije en la parte superior, las aplicaciones Java de proceso único no pueden ofrecer ninguna de esas garantías, pero al menos pueden ser un poco más robustas que las predeterminadas.

Dicho esto, su ejemplo particular (serialización) puede no ser un buen candidato para este enfoque. En particular, lo primero que el usuario querría hacer al saber que hay un problema es guardar su trabajo: pero si la serialización está fallando, puede que sea imposible de guardar. Eso no es lo que quiere, por lo que podría tener que hacer algunos experimentos y / o cálculos, y restringir manualmente cuántos millones de elementos permite su progtwig (en función de la cantidad de memoria con la que se está ejecutando), antes del punto donde intenta serializar.

Esto es más sólido que tratar de detectar el error y continuar, pero desafortunadamente es difícil calcular el límite exacto, por lo que probablemente tendrías que equivocarte por precaución.

Si el error se produce durante la deserialización, entonces está mucho más firme: no cargar un archivo no debería ser un error fatal en una aplicación si es posible evitarlo. Es más probable que capturar el error sea apropiado.

Hagas lo que hagas para manejar la falta de recursos (incluso dejar que el Error elimine la aplicación), si te importan las consecuencias, entonces es realmente importante probarlo a fondo. La dificultad es que nunca se sabe exactamente en qué punto del código se producirá el problema, por lo que generalmente hay una gran cantidad de estados del progtwig que deben probarse.

No deberías manejarlo en código. OutOfMemory no debe ser capturado y manejado. En su lugar, inicie su JVM con un espacio de stacks más grande

 java -Xmx512M 

debería hacer el truco.

Mira aquí para más detalles

Todos los demás ya han explicado cómo darle a Java más memoria, pero dado que “manejar” podría significar captura, voy a citar lo que Sun tiene que decir sobre Error s:

Un Error es una subclase de Throwable que indica problemas graves que una aplicación razonable no debería intentar atrapar . La mayoría de esos errores son condiciones anormales.

(énfasis mío)

Obtiene un OutOfMemoryError porque su progtwig requiere más memoria de la que JVM tiene disponible. No hay nada que puedas hacer específicamente en tiempo de ejecución para ayudar con esto.

Como señaló krosenvold, su aplicación puede exigir sensiblemente la memoria, pero sucede que la JVM no se está iniciando lo suficiente (por ejemplo, su aplicación tendrá una huella de memoria de 280MB pero la JVM solo comienza con 256MB). En este caso, boost el tamaño asignado resolverá esto.

Si cree que está suministrando la memoria adecuada al inicio, es posible que la aplicación esté usando demasiada memoria de forma transitoria o tenga una pérdida de memoria. En la situación que ha publicado, parece que mantiene referencias a todos los millones de elementos en la memoria a la vez, aunque es posible que los trate secuencialmente.

Comprueba cómo son tus referencias para los artículos que están “terminados”; debes respetarlos lo antes posible para que puedan ser recolectados. Si agrega un millón de elementos a una colección y luego itera sobre esa colección, por ejemplo, necesitará suficiente memoria para almacenar todas esas instancias de objetos. Vea si puede tomar un objeto a la vez, serializarlo y luego descartar la referencia.

Si tiene problemas para resolver esto, publicar un fragmento de código de pseudo ayudaría.

Además de algunos de los consejos que se le han brindado, como revisión, la memoria carece y también inicia la JVM con más memoria (-Xmx512M). Parece que tiene un OutOfMemoryError porque su TopicParser está leyendo una línea que probablemente sea bastante grande (y esto es lo que debe evitar), puede usar el FileReader (o, si la encoding es un problema, un InputStreamReader que envuelve un FileInputStream ). Utilice su método de lectura (char []) con una matriz de caracteres [] de tamaño razonable como un búfer.

También, finalmente, para investigar un poco por qué es OutOfMemoryError puede usar -XX: + HeapDumpOnOutOfMemoryError Flag en la JVM para obtener una información de stack de volcado en el disco.

¡Buena suerte!

Use la palabra clave transitoria para marcar campos en las clases serializadas que se pueden generar a partir de los datos existentes. Implemente writeObject y readObject para ayudar a reconstruir datos transitorios.

Interesante: te estás quedando sin memoria en una línea de lectura. En una suposición, estás leyendo en un archivo grande sin saltos de línea.

En lugar de utilizar readline para sacar las cosas del archivo como una única cadena larga grande, escriba cosas que entiendan mejor la entrada, y las maneje en trozos.

Si simplemente debe tener todo el archivo en una sola cadena larga … bueno, mejore en la encoding. En general, tratar de manejar los datos mutimegabyte rellenando todo en una única matriz de bytes (o lo que sea) es una buena manera de perder.

Eche un vistazo a CharacterSequence.

Después de seguir la sugerencia de boost el espacio de montón (a través de -Xmx), pero asegúrese de utilizar JConsole o JVisualVM para perfilar el uso de la memoria de las aplicaciones. Asegúrese de que el uso de la memoria no crezca continuamente. Si es así, aún obtendrás OutOfMemoryException, solo te llevará más tiempo.

Comience Java con un valor mayor para la opción -Xmx, por ejemplo -Xmx512m

No hay una manera real de manejarlo bien. Una vez que sucede, estás en el territorio desconocido. Usted puede decir por el nombre – OutOfMemory Error . Y se describe como:

Lanzada cuando la Máquina Virtual Java no puede asignar un objeto porque está sin memoria, y el recolector de basura no puede poner más memoria disponible

Usualmente OutOfMemoryError indica que hay algo muy mal con el sistema / enfoque (y es difícil señalar una operación particular que lo activó).

Muy a menudo tiene que ver con el agotamiento ordinario del espacio aéreo. Usando el -verbosegc y mencionado anteriormente -XX: + HeapDumpOnOutOfMemoryError debería ayudar.

Puede encontrar un resumen breve y conciso del problema en javaperformancetuning

Antes de tomar cualquier acción estratégica o que requiera mucho tiempo, debe establecer exactamente qué parte de su progtwig consume tanta memoria. Puede pensar que sabe la respuesta, pero hasta que tenga evidencia en frente de usted, no la tiene. Existe la posibilidad de que la memoria esté siendo utilizada por algo que no estabas esperando.

Use un perfilador. No importa cuál, hay muchos . Primero averigua cuánta memoria está consumiendo cada objeto. Segundo, repita las iteraciones de su serializador, compare las instantáneas de memoria y vea qué objetos o datos se crean.

La respuesta probablemente sea transmitir la salida en lugar de construirla en la memoria. Pero obtén evidencia primero.

Puede boost el tamaño de la memoria que usa java con la opción -Xmx, por ejemplo:

 java -Xmx512M -jar myapp.jar 

Mejor es reducir la huella de memoria de su aplicación. ¿Serializas millones de artículos? ¿Necesitas mantenerlos todos en la memoria? ¿O puede liberar algunos de ellos después de usarlos? Intenta reducir los objetos usados.

He descubierto una alternativa, respetando todos los demás puntos de vista, de que no deberíamos tratar de atrapar la memoria por excepción, esto es lo que he aprendido en los últimos tiempos.

 catch (Throwable ex){ if (!(ex instanceof ThreadDeath)) { ex.printStackTrace(System.err); }} 

para su referencia: OutOfMemoryError cualquier comentario es bienvenido.

Avishek Arang