Comprender la excepción OutOfMemoryError en Java

En Java, todos los objetos se almacenan en un montón. Se asignan utilizando un nuevo operador. La excepción OutOfMemoryError en Java tiene este aspecto: 

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

Por lo general, este error se produce cuando la máquina virtual de Java no puede asignar un objeto porque no tiene memoria. El recolector de elementos no utilizados no pudo poner a disposición más memoria.

OutOfMemoryError generalmente significa que está haciendo algo mal, ya sea reteniendo objetos demasiado tiempo o tratando de procesar demasiados datos a la vez. A veces, indica un problema que está fuera de su control, como una biblioteca de terceros que almacena strings en caché o un servidor de aplicaciones que no se limpia después de las implementaciones. Y a veces, no tiene nada que ver con los objetos del montón.

La excepción java.lang.OutOfMemoryError también puede ser lanzada por el código de la biblioteca nativa cuando no se puede satisfacer una asignación nativa (por ejemplo, si el espacio de intercambio es bajo). Comprendamos varios casos en los que podría ocurrir el error OutOfMemory.

¿Síntoma o causa raíz?

Para encontrar la causa, el texto de la excepción incluye un mensaje detallado al final. Examinemos todos los errores. 

Error 1: espacio de almacenamiento dinámico de Java: 

Este error surge debido a las aplicaciones que hacen un uso excesivo de los finalizadores. Si una clase tiene un método de finalización, los objetos de ese tipo no recuperan su espacio en el momento de la recolección de elementos no utilizados . En su lugar, después de la recolección de elementos no utilizados, los objetos se ponen en cola para su finalización, lo que ocurre más tarde. 

Implementación: 

  • los finalizadores son ejecutados por un subproceso daemon que da servicio a la cola de finalización.
  • Si el subproceso del finalizador no puede seguir el ritmo de la cola de finalización, el almacenamiento dinámico de Java podría llenarse y se generaría este tipo de excepción OutOfMemoryError.
  • El problema también puede ser tan simple como un problema de configuración, donde el tamaño de almacenamiento dinámico especificado (o el tamaño predeterminado, si no se especifica) es insuficiente para la aplicación.

Java

// Java program to illustrate
// Heap error
 
import java.util.*;
 
public class Heap {
    static List<String> list = new ArrayList<String>();
 
    public static void main(String args[]) throws Exception
    {
        Integer[] array = new Integer[10000 * 10000];
    }
}

Producción:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at Heap.main(Heap.java:11)

Cuando ejecuta el código anterior, puede esperar que se ejecute para siempre sin ningún problema. Como resultado, con el tiempo, con el uso constante del código de fuga, los resultados «almacenados en caché» terminan consumiendo una gran cantidad de espacio de almacenamiento dinámico de Java, y cuando la memoria filtrada llena toda la memoria disponible en la región de almacenamiento dinámico y la recolección de basura no puede para limpiarlo, se lanza el espacio de almacenamiento dinámico java.lang.OutOfMemoryError:Java .

Prevención: Verifique cómo monitorear los objetos que están pendientes de finalización en Monitorear los objetos pendientes de finalización.

Error 2: se excedió el límite de gastos generales del GC: 

Este error indica que el recolector de basura se está ejecutando todo el tiempo y que el programa Java está progresando muy lentamente. Después de una recolección de elementos no utilizados, si el proceso Java dedica más de aproximadamente el 98 % de su tiempo a la recolección de elementos no utilizados y si está recuperando menos del 2 % del almacenamiento dinámico y ha estado realizando hasta ahora los últimos 5 (constante de tiempo de compilación) consecutivos recolecciones de basura, luego se lanza  un java.lang.OutOfMemoryError .

Esta excepción generalmente se produce porque la cantidad de datos en vivo apenas cabe en el montón de Java que tiene poco espacio libre para nuevas asignaciones. 

Java

// Java program to illustrate
// GC Overhead limit exceeded
 
import java.util.*;
 
public class Wrapper {
    public static void main(String args[]) throws Exception
    {
        Map m = new HashMap();
        m = System.getProperties();
        Random r = new Random();
       
        while (true) {
            m.put(r.nextInt(), "randomValue");
        }
    }
}

Si ejecuta este programa con java -Xmx100m -XX:+UseParallelGC Wrapper , el resultado será algo como esto: 

Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.Integer.valueOf(Integer.java:832)
    at Wrapper.main(error.java:9)

Prevención: aumente el tamaño del almacenamiento dinámico y desactívelo con el indicador de línea de comandos -XX:-UseGCOverheadLimit. 

Error 3: se lanza el espacio de Permgen: 

La memoria de Java está separada en diferentes regiones. El tamaño de todas esas regiones, incluida el área de permgen, se establece durante el lanzamiento de JVM. Si no establece los tamaños usted mismo, se utilizarán los valores predeterminados específicos de la plataforma. 

El error de espacio java.lang.OutOfMemoryError : PermGen indica que el área de la generación permanente en la memoria está agotada. 

Java

// Java program to illustrate
// Permgen Space error
 
import javassist.ClassPool;
 
public class Permgen {
    static ClassPool classPool = ClassPool.getDefault();
 
    public static void main(String args[]) throws Exception
    {
        for (int i = 0; i < 1000000000; i++) {
            Class c = classPool.makeClass("com.saket.demo.Permgen" + i).toClass();
            System.out.println(c.getName());
        }
    }
}

En el código de muestra anterior, el código itera sobre un bucle y genera clases en tiempo de ejecución. La biblioteca  Javassist se ocupa de la complejidad de la generación de clases.

Ejecutar el código anterior seguirá generando nuevas clases y cargando sus definiciones en el espacio Permgen hasta que el espacio se utilice por completo y se arroje el espacio java.lang.OutOfMemoryError : Permgen. 

Prevención: cuando se produce el OutOfMemoryError debido al agotamiento de PermGen durante el inicio de la aplicación, la solución es simple. La aplicación solo necesita más espacio para cargar todas las clases en el área PermGen, por lo que debemos aumentar su tamaño. Para hacerlo, modifique la configuración de inicio de su aplicación y agregue (o aumente, si está presente) el parámetro -XX:MaxPermSize similar al siguiente ejemplo: 

java -XX:MaxPermSize=512m com.saket.demo.Permgen

Error 4 – Metaespacio: 

Los metadatos de la clase Java se asignan en la memoria nativa. Supongamos que se agota el metaespacio para los metadatos de la clase, se genera una excepción java.lang.OutOfMemoryError con un MetaSpace detallado. 

La cantidad de metaespacio utilizado para los metadatos de clase está limitada por el parámetro MaxMetaSpaceSize, que se especifica en la línea de comandos. Cuando la cantidad de memoria nativa necesaria para los metadatos de una clase excede MaxMetaSpaceSize, se lanza una excepción java.lang.OutOfMemoryError con un MetaSpace detallado.

Java

// Java program to illustrate
// Metaspace error
 
import java.util.*;
 
public class Metaspace {
    static javassist.ClassPool cp
        = javassist.ClassPool.getDefault();
 
    public static void main(String args[]) throws Exception
    {
        for (int i = 0; i < 100000; i++) {
            Class c = cp.makeClass(
                            "com.saket.demo.Metaspace" + i)
                          .toClass();
        }
    }
}

Este código seguirá generando nuevas clases y cargando sus definiciones en Metaspace hasta que el espacio se utilice por completo y se produzca java.lang.OutOfMemoryError: Metaspace. Cuando se inicia con -XX:MaxMetaspaceSize=64m, luego en Mac OS X, mi Java 1.8.0_05 muere alrededor de las 70 000 clases cargadas.

Prevención: si se ha configurado MaxMetaSpaceSize en la línea de comando, aumente su valor. MetaSpace se asigna desde los mismos espacios de direcciones que el montón de Java. Reducir el tamaño del montón de Java hará que haya más espacio disponible para MetaSpace. Esta es solo una compensación correcta si hay un exceso de espacio libre en el almacenamiento dinámico de Java. 

Error 5: el tamaño de array solicitado excede el límite de VM: 

Este error indica que la aplicación intentó asignar una array que es más grande que el tamaño del almacenamiento dinámico. Por ejemplo, si una aplicación intenta asignar una array de 1024 MB, pero el tamaño máximo del almacenamiento dinámico es de 512 MB, se generará un error OutOfMemoryError con «El tamaño de la array solicitada supera el límite de VM». 

Java

// Java program to illustrate
// Requested array size
// exceeds VM limit error
 
import java.util.*;
 
public class GFG {
    static List<String> list = new ArrayList<String>();
 
    public static void main(String args[]) throws Exception
    {
        Integer[] array = new Integer[10000 * 10000];
    }
}

Producción:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at GFG.main(GFG.java:12)

El java.lang.OutOfMemoryError: El tamaño de array solicitado excede el límite de VM puede aparecer como resultado de cualquiera de las siguientes situaciones: 

  • Sus arreglos crecen demasiado y terminan teniendo un tamaño entre el límite de la plataforma y el Integer.MAX_INT
  • Intenta deliberadamente asignar arrays de más de 2^31-1 elementos para experimentar con los límites.

Error 6: solicitar bytes de tamaño por un motivo. ¿Sin espacio de intercambio?: 

Esta aparente excepción ocurrió cuando una asignación del montón nativo falló y el montón nativo podría estar cerca del agotamiento. El error indica el tamaño (en bytes) de la solicitud que falló y el motivo de la solicitud de memoria. Por lo general, el motivo es el nombre del módulo de origen que notifica el error de asignación, aunque a veces es el motivo real. 

El error java.lang.OutOfMemoryError: Out of swap space a menudo es causado por problemas a nivel del sistema operativo, como: 

  • El sistema operativo está configurado con espacio de intercambio insuficiente.
  • Otro proceso en el sistema está consumiendo todos los recursos de memoria.

Prevención: cuando se lanza este mensaje de error, la VM invoca el mecanismo de manejo de errores fatales (es decir, genera un archivo de registro de errores fatales, que contiene información útil sobre el subproceso, el proceso y el sistema en el momento del bloqueo). En el caso del agotamiento del almacenamiento dinámico nativo, la memoria del almacenamiento dinámico y la información del mapa de memoria en el registro pueden ser útiles.

Error 7: motivo stack_trace_with_native_method: 

Cada vez que se lanza este mensaje de error (razón stack_trace_with_native_method), se imprime un seguimiento de pila en el que el marco superior es un método nativo, entonces esto es una indicación de que un método nativo ha encontrado una falla de asignación. La diferencia entre este mensaje y el anterior es que el error de asignación se detectó en una interfaz nativa de Java (JNI) o en un método nativo en lugar del código JVM. 

Java

// Java program to illustrate
// new native thread error
 
import java.util.*;
 
public class GFG {
    public static void main(String args[]) throws Exception
    {
        while (true) {
            new Thread(new Runnable() {
                public void run()
                {
                    try {
                        Thread.sleep(1000000000);
                    }
                    catch (InterruptedException e) {
                    }
                }
            }).start();
        }
    }
}

El límite exacto de subprocesos nativos depende de la plataforma. Por ejemplo, las pruebas de Mac OS X revelan que: Mac OS X 10.9 de 64 bits, Java 1.7.0_45: JVM muere después de que se hayan creado los subprocesos #2031

Prevención: use las utilidades nativas del sistema operativo para diagnosticar el problema con más detalle. Para obtener más información sobre las herramientas disponibles para varios sistemas operativos, consulte Herramientas del sistema operativo nativo

Este artículo es una contribución de Saket Kumar . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks. Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *