Entrevistador: ¿Qué escenarios producirán OOM? ¿Cómo lidiar con ello?

Un amigo se encontró con esta pregunta de la entrevista durante la entrevista ¿Cuándo se lanzará la excepción OutOfMemery? A primera vista parece bastante simple. De hecho, es la comprensión de toda la JVM lo que se investiga en profundidad, y esta pregunta se puede convertir en algunas respuestas desordenadas de Internet. De hecho, en resumen, básicamente se pueden resumir 4 escenarios.

Desbordamiento de memoria del montón

El desbordamiento de la memoria del montón es demasiado común, y la mayoría de la gente debería poder pensar en esto. La memoria del montón se usa para almacenar instancias de objetos. Mientras sigamos creando objetos y nos aseguremos de que haya una ruta accesible entre las raíces de GC y los objetos para evitar la recolección de basura, entonces Esta excepción se produce poco después de que el número de objetos supere el límite de tamaño del montón máximo.

Escriba un fragmento de código para probar y establezca el tamaño de la memoria del montón en 2M.

public class HeapOOM {
    
    
    public static void main(String[] args) {
    
    
        List<HeapOOM> list = new ArrayList<>();
        while (true) {
    
    
            list.add(new HeapOOM());
        }
    }
}

Ejecute el código y pronto verá que aparece la excepción OOM. La sugerencia aquí es que el espacio del montón de Java se desborda.

El método general de resolución de problemas se puede analizar configurando -XX: + HeapDumpOnOutOfMemoryError para volcar la instantánea de volcado de memoria actual cuando ocurre una excepción. El análisis se puede analizar usando Eclipse Memory Analyzer (MAT), y los archivos independientes se pueden descargar en el sitio web oficial .

Además, si está utilizando IDEA, puede utilizar la versión comercial de JProfiler o la versión de código abierto de JVM-Profiler. Además, IDEA2018 tiene herramientas de análisis integradas, incluyendo Flame Graph y Call Tree.

Gráfico de llama

Área de métodos (grupo de constantes en tiempo de ejecución) y desbordamiento de metaespacio

El área de método, como el montón, es un área compartida por subprocesos, incluida la información del archivo de clase, el grupo de constantes de tiempo de ejecución y el grupo de constantes. La principal diferencia entre el grupo de constantes de tiempo de ejecución y el grupo de constantes es que es dinámico, es decir, no tiene que estar en el archivo de clase. El contenido del grupo de constantes puede ingresar al grupo de constantes de tiempo de ejecución, y también se pueden poner nuevas constantes en el grupo durante el tiempo de ejecución, como el método intern () de String.

Escribamos un fragmento de código para verificar String.intern (), y establecemos -XX: MetaspaceSize = 50m -XX: MaxMetaspaceSize = 50m de tamaño de metaespacio. Como estoy usando la versión 1.8 del JDK, el área de método existe en la generación permanente (PermGen) antes de la versión 1.8. Después de 1.8, el concepto de generación permanente se cancela y se convierte a Metaspace. Si es la versión anterior, puede configurar PermSize MaxPermSize permanente El tamaño de la generación.

 private static String str = "test";
    public static void main(String[] args) {
    
    
        List<String> list = new ArrayList<>();
        while (true){
    
    
            String str2 = str + str;
            str = str2;
            list.add(str.intern());
        }
}

Ejecute el código, encontrará que el código informa errores.

Modifique la configuración nuevamente, elimine el límite de metaespacio, modifique el tamaño de la memoria del montón -Xms20m -Xmx20m, puede ver el error de la memoria del montón.

¿Por qué es esto? intern () en sí es un método nativo, su función es: si el grupo de constantes de cadena ya contiene una cadena igual a este objeto String, devolverá el objeto String que representa la cadena en el grupo; de lo contrario, este objeto String se incluirá La cadena se agrega al grupo constante y se devuelve una referencia al objeto String.

Después de la versión 1.7, el grupo constante de cadenas se ha transferido al área del montón, por lo que se informará un error de desbordamiento de la memoria del montón. Si la versión es anterior a 1.7, verá un error de espacio de PermGen.

Desbordamiento de memoria directa

La memoria directa no forma parte del área de datos de la máquina virtual cuando se está ejecutando y no está limitada por la memoria dinámica, sino por el tamaño de la memoria de la máquina. Por ejemplo, en NIO, las funciones nativas se pueden usar para asignar directamente memoria fuera del montón, lo que fácilmente conduce a problemas OOM.

El tamaño de la memoria directa se puede especificar mediante -XX: MaxDirectMemorySize; si no se especifica, el valor predeterminado es el mismo que el tamaño máximo de almacenamiento dinámico de Java -Xmx.

Una característica obvia del desbordamiento de memoria causado por la memoria directa es que no se observarán anomalías obvias en el archivo de volcado. Si se encuentra que el archivo de volcado es muy pequeño después de OOM y se usa NIO directa o indirectamente en el programa, entonces puede considerar verificarlo. ¿Es la razón de esto?

Desbordamiento de memoria de pila

La pila es privada para el hilo y su ciclo de vida es el mismo que el del hilo. Cuando se ejecuta cada método, se crea un marco de pila para almacenar información como tabla de variables locales, pila de operandos, enlace dinámico, salida de método, etc. El proceso de invocación de método es el proceso de empujar y hacer estallar el marco de pila.

En la especificación de la máquina virtual Java, se definen dos excepciones para la pila de la máquina virtual:

  1. Si la profundidad de la pila solicitada por el hilo es mayor que la profundidad permitida por la máquina virtual, se lanzará una excepción StackOverflowError
  2. Si la pila de la máquina virtual se puede expandir dinámicamente y no se puede solicitar suficiente memoria durante la expansión, se lanzará una excepción OutOfMemoryError

Primero escriba un fragmento de código para probarlo, establezca -Xss160k, -Xss representa el tamaño de la memoria de pila de cada hilo

public class StackOOM {
    
    
    private int length = 1;

    public void stackTest() {
    
    
        System.out.println("stack lenght=" + length);
        length++;
        stackTest();
    }

    public static void main(String[] args) {
    
    
        StackOOM test = new StackOOM();
        test.stackTest();
    }
}

La prueba encontró que no importa cómo configurar los parámetros en un solo hilo, StackOverflow es anormal.

Intente modificar el código para que tenga varios subprocesos y ajuste -Xss2m, porque cuanto mayor sea la memoria asignada para cada subproceso, menor será el número de subprocesos que se pueden acomodar en el espacio de la pila y más fácil es provocar un desbordamiento de memoria. Por el contrario, si la memoria es insuficiente, este parámetro se puede reducir para lograr el propósito de soportar más subprocesos.

public class StackOOM {
    
    
    private void dontStop() {
    
    
        while (true) {
    
    
        }
    }

    public void stackLeakByThread() {
    
    
        while (true) {
    
    
            new Thread(() -> dontStop()).start();
        }
    }

    public static void main(String[] args) throws Throwable {
    
    
        StackOOM stackOOM = new StackOOM();
        stackOOM.stackLeakByThread();
    }
}

Obtenga más conocimientos técnicos interesantes, preste atención a la cuenta oficial: Ciencia y Tecnología Miaomiao

Supongo que te gusta

Origin blog.csdn.net/awl910213/article/details/108640054
Recomendado
Clasificación