2012-08-23 12 views
28

he oído cosas muy contradictorias sobre cómo manejar mejor esto, y estoy atascado con el siguiente dilema:Java - apagar el error de memoria

  • un OOME hace que caiga un hilo, pero no el toda la aplicación
  • y necesito para derribar toda la aplicación, pero no pueden porque el hilo no tiene ningún memoria dejó

siempre he entendido mejor práctica es que se vayan por lo que la JVM puede morir porque la JVM está en un estado inconsistente en ese punto, pero eso no parece estar trabajando aquí.

+0

Sobre todo lo que puedo decir es que el manejo de un error de falta de memoria es muy * * difícil. Cualquier controlador que tenga debe tener cuidado de no crear CUALQUIER objeto nuevo: use objetos creados previamente (y tenga cuidado con las modificaciones que puedan hacer asignaciones). –

Respuesta

4

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

En general, nunca he tenido un poco de suerte para manejar con gracia 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.

0

Puede poner en su código de rosca con un intento de captura para el OOME y hacer algo de limpieza manual si se produce 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 lo hace parte de la recopilación de elementos en él, debe tener más que suficiente espacio libre para otros hilos para dejar de fumar de una manera ordenada. Este es un abandono más elegante con la oportunidad de registrar el problema antes de morir también.

4

Puede forzar 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 usar Eclipse MAT como un producto independiente, consulte: http://wiki.eclipse.org/index.php/MemoryAnalyzer.

+0

Estoy de acuerdo con el volcado de memoria para analizar la causa. No estoy de acuerdo con editar el código de System.exit. En mi opinión, esta estrategia es peligrosa, así que si la elegimos, prefiero usar opciones en la línea de comando, ya que el integrador las verá y se sorprenderá menos (y podrá cambiarlas). – mcoolive

30

OutOfMemoryError es como cualquier otro error. Si escapa de Thread.run(), provocará la muerte del hilo. 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 no importa qué, porque se sospecha que puede estar en un estado incoherente, añadir esto a sus java opciones:

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

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

Por supuesto, también se puede tratar de atrapar OutOfMemoryError y gastos de alguna manera. Pero eso es complicado.

+11

Advertencia: un kill -9 abrupto puede anular el proceso antes de que los registros se hayan descargado, por lo que puede parecer un bloqueo sin indicación de lo sucedido. Sería inteligente usar una cadena de comandos que intenten un cierre educado "stop.sh% p" donde el script de detención puede registrar que está a punto de matar el proceso, luego pruebe con "kill -TERM $ 1", luego duerma, y ​​luego haga matar -9 al último. De esta forma, no experimentarás misteriosos bloqueos cuando tu JVM se suicide sin registrar lo que estaba haciendo justo antes en los registros principales. – simbo1905

1

En general, nunca 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 el JavaDoc para java.lang.Error:

Un error es una subclase de Throwable que indica problemas serios que una aplicación razonable no debe tratar de atrapar.

Si tiene problemas con que su aplicación continúe ejecutándose incluso después de que uno de los hilos muera 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 hacerlo, deberá llamar al setDaemon(true) en el objeto de hilo 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 controlador de excepciones no detectadas a los hilos en cuestión y llamar al 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.

-2

No debe manejar OOM de ninguna manera. Debe corregir. 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 this blog series, eso no es tan fácil. Pero puedes probar eso Plumbr. Debería funcionar mejor que otros :)

+2

y parte de mi aplicación es tolerante a fallas está haciendo lo correcto cuando suceden, que es el punto de esta pregunta. – djechlin

+2

Es posible quedarse sin memoria sin tener una fuga. Se llama carga. – kylejmcintyre

1

Sugiero que maneje todas las excepciones no detectadas desde dentro de la aplicación para asegurarse de que intenta darle la mejor información posible antes de terminar. 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; 
    } 
} 
+0

Esto es al menos un intento de dejar un rastro útil. Sin embargo, si este código se ejecuta en otro OOM, me pregunto si obtendrá un bucle de recursión de controlador de excepción de infite. – Harald

+0

Puede asegurarse de que no lo haga si desactiva la excepción predeterminada en la primera línea del controlador. – Shloim

26

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

Desde el release notes:

ExitOnOutOfMemoryError - Cuando se 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.se añadieron

14

En la versión de Java VM 8u92 argumentos del

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

, ver la release notes.

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

CrashOnOutOfMemoryError
Si esta opción está activada, cuando se produce un error fuera de la memoria, los accidentes de JVM y produce texto y archivos binarios de choque.

Mejora de pedidos: JDK-8138745 (parámetro de denominación es incorrecta aunque JDK-8154713, ExitOnOutOfMemoryError en lugar de ExitOnOutOfMemory)