studio programacion móviles ejemplo desarrollo curso aplicaciones java out-of-memory

programacion - Java: se está cerrando el error de falta de memoria



manual de programacion android pdf (10)

Con la versión 8u92 ahora hay una opción de JVM en Oracle JDK para hacer que la JVM salga cuando se produce un OutOfMemoryError:

De las notas de la versión :

ExitOnOutOfMemoryError : cuando habilita esta opción, la JVM sale en la primera aparición de un error de falta de memoria. Se puede usar si prefiere reiniciar una instancia de la JVM en lugar de gestionar los errores de falta de memoria.

He escuchado cosas muy contradictorias sobre cómo manejar mejor esto, y estoy atascado con el siguiente dilema:

  • un OOME trae un hilo, pero no toda la aplicación
  • y necesito bajar la aplicación completa pero no puedo porque el hilo no tiene ningún recuerdo

Siempre he entendido que la mejor práctica es dejarlos ir para que la JVM pueda morir porque la JVM está en un estado inconsistente en ese momento, pero eso no parece funcionar aquí.


Desde las opciones de JVM

-XX:+ExitOnOutOfMemoryError

-XX:+CrashOnOutOfMemoryError

-XX:OnOutOfMemoryError=...

no funciona si OutOfMemoryError se produce debido a subprocesos agotados (consulte JDK-8138745 ), puede valer la pena probar la herramienta jkill . Se registra a través de JVMTI y sale de la VM si la memoria o los hilos disponibles están agotados.

En mis pruebas funciona como se esperaba (y cómo esperaría que las opciones de JVM funcionaran).


En Java versión 8u92, los argumentos de VM

  • -XX:+ExitOnOutOfMemoryError
  • -XX:+CrashOnOutOfMemoryError

se agregaron, vea las notas de la versión .

ExitOnOutOfMemoryError
Cuando habilita esta opción, la JVM sale en la primera aparición de un error de falta de memoria. Se puede usar si prefiere reiniciar una instancia de la JVM en lugar de gestionar los errores de falta de memoria.

CrashOnOutOfMemoryError
Si esta opción está habilitada, cuando ocurre un error de falta de memoria, la JVM falla y produce archivos de bloqueo de texto y binarios.

Solicitud de mejora: JDK-8138745 (la nomenclatura de los parámetros es incorrecta, aunque JDK-8154713 , ExitOnOutOfMemoryError lugar de ExitOnOutOfMemory )


En general, nunca se debe escribir un bloque catch que capture java.lang.Error o cualquiera de sus subclases, incluido OutOfMemoryError . La única excepción a esto sería si está utilizando una biblioteca de terceros que arroja una subclase personalizada de Error cuando deberían haber subclasificado RuntimeException . Sin embargo, esto es solo una solución para un error en su código.

Desde JavaDoc para java.lang.Error :

Un error es una subclase de Throwable que indica problemas graves que una aplicación razonable no debería intentar atrapar.

Si tiene problemas con su aplicación que continúa ejecutándose incluso después de que uno de los hilos muere debido a un OOME, tiene un par de opciones.

En primer lugar, es posible que desee comprobar si es posible marcar los hilos restantes como hilos daemon. Si alguna vez hay un punto en el que solo los hilos daemon permanecen en la JVM, ejecutará todos los enganches de cierre y terminará lo más ordenadamente posible. Para hacer esto, necesitará llamar a setDaemon(true) en el objeto de la setDaemon(true) antes de que se inicie. Si los hilos son realmente creados por un marco o algún otro código, puede que tenga que usar un medio diferente para establecer ese indicador.

La otra opción es asignar un manejador de excepción no capturado a los hilos en cuestión y llamar a System.exit() o si es absolutamente necesario Runtime.getRuntime().halt() . La detención de llamadas es muy peligrosa ya que los ganchos de apagado ni siquiera intentarán ejecutarse, pero en ciertas situaciones la detención podría funcionar donde System.exit hubiera fallado si ya se hubiera lanzado un OOME.


No debe manejar OOM de ninguna manera. Debes arreglarlo . Para eso, naturalmente, debe encontrar una causa raíz de la pérdida de memoria: qué objetos están goteando y por qué. Desafortunadamente, como se puede ver en esta serie de blogs , no es tan fácil. Pero puedes intentarlo con Plumbr . Debería funcionar mejor que otros :)


Puede obligar a su programa a terminar de múltiples maneras, una vez que ocurra el error. Como otros han sugerido, puede detectar el error y hacer un System.exit después de eso, si es necesario. Pero le sugiero que también use -XX: + HeapDumpOnOutOfMemoryError, de esta manera la JVM creará un archivo de volcado de memoria con el contenido de su aplicación una vez que se haya producido el evento. Utilizará un perfil, le recomiendo Eclipse MAT para investigar la imagen. De esta forma, descubrirá muy rápidamente cuál es la causa del problema y reaccionará correctamente. Si no está utilizando Eclipse, puede utilizar Eclipse MAT como un producto independiente, consulte: http://wiki.eclipse.org/index.php/MemoryAnalyzer .


Puede rodear su código de hilo con un try catch para el OOME y hacer una limpieza manual si ocurre un evento de este tipo. Un truco es hacer que su función de hilo sea solo una captura de prueba alrededor de otra función. En caso de error de memoria, debería liberar algo de espacio en la pila, lo que le permite hacer algunas eliminaciones rápidas. Esto debería funcionar si realiza una solicitud de recolección de elementos no utilizados en algunos recursos inmediatamente después de la captura y / o establece un indicador de extinción para que otros hilos se detengan.

Una vez que el hilo con OOME muere y usted hace una cierta colección en sus elementos, debe tener más que suficiente espacio libre para que otros hilos salgan de manera ordenada. Este es un abandono más elegante con la oportunidad de registrar el problema antes de morir también.


Si desea cerrar su programa, eche un vistazo a la -XX:OnOutOfMemoryError="<cmd args>;<cmd args>" ( documentada aquí ) en la línea de comando. Simplemente apúntalo a un script de destrucción para tu aplicación.

En general, nunca tuve la suerte de manejar correctamente este error sin reiniciar la aplicación. Siempre hubo algún tipo de caso de esquina deslizándose, así que personalmente sugiero que detenga su aplicación, pero investigue el origen del problema.


Sugiero manejar todas las excepciones no detectadas desde dentro de la aplicación para garantizar que intente brindarle la mejor información posible antes de finalizar. Luego, tenga una secuencia de comandos externa que reinicie su proceso cuando falle.

public class ExitProcessOnUncaughtException implements UncaughtExceptionHandler { static public void register() { Thread.setDefaultUncaughtExceptionHandler(new ExitProcessOnUncaughtException()); } private ExitProcessOnUncaughtException() {} @Override public void uncaughtException(Thread t, Throwable e) { try { StringWriter writer = new StringWriter(); e.printStackTrace(new PrintWriter(writer)); System.out.println("Uncaught exception caught"+ " in thread: "+t); System.out.flush(); System.out.println(); System.err.println(writer.getBuffer().toString()); System.err.flush(); printFullCoreDump(); } finally { Runtime.getRuntime().halt(1); } } public static void printFullCoreDump() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("/n"+ sdf.format(System.currentTimeMillis())+"/n"+ "All Stack Trace:/n"+ getAllStackTraces()+ "/nHeap/n"+ getHeapInfo()+ "/n"); } public static String getAllStackTraces() { String ret=""; Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); for (Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet()) ret+=getThreadInfo(entry.getKey(),entry.getValue())+"/n"; return ret; } public static String getHeapInfo() { String ret=""; List<MemoryPoolMXBean> memBeans = ManagementFactory.getMemoryPoolMXBeans(); for (MemoryPoolMXBean mpool : memBeans) { MemoryUsage usage = mpool.getUsage(); String name = mpool.getName(); long used = usage.getUsed(); long max = usage.getMax(); int pctUsed = (int) (used * 100 / max); ret+=" "+name+" total: "+(max/1000)+"K, "+pctUsed+"% used/n"; } return ret; } public static String getThreadInfo(Thread thread, StackTraceElement[] stack) { String ret=""; ret+="/n/""+thread.getName()+"/""; if (thread.isDaemon()) ret+=" daemon"; ret+= " prio="+thread.getPriority()+ " tid="+String.format("0x%08x", thread.getId()); if (stack.length>0) ret+=" in "+stack[0].getClassName()+"."+stack[0].getMethodName()+"()"; ret+="/n java.lang.Thread.State: "+thread.getState()+"/n"; ret+=getStackTrace(stack); return ret; } public static String getStackTrace(StackTraceElement[] stack) { String ret=""; for (StackTraceElement element : stack) ret+="/tat "+element+"/n"; return ret; } }


OutOfMemoryError es como cualquier otro error. Si escapa de Thread.run() hará que el hilo muera. Nada mas. Además, cuando un hilo muere, ya no es una raíz de GC, por lo que todas las referencias guardadas solo por este hilo son elegibles para la recolección de basura. Esto significa que es muy probable que JVM se recupere de OOME.

Si quieres matar a tu JVM sin importar nada porque sospechas que puede estar en un estado incoherente, agrégala a tus opciones de java :

-XX:OnOutOfMemoryError="kill -9 %p"

%p es el marcador de posición de PID del proceso de Java actual. El resto se explica por sí mismo.

Por supuesto, también puedes intentar detectar OutOfMemoryError y manejarlo de alguna manera. Pero eso es complicado.