[JVM advanced road] cuatro: desbordamiento de memoria y pérdida de memoria

En Java, hay dos problemas principales relacionados con la memoria , desbordamiento de memoria y pérdida de memoria .

  • Fuera de la memoria ( Memoria insuficiente ) : Cuando se aplica para la memoria, la JVM no tiene suficiente espacio de memoria. El dicho popular es ir al pozo y encontrar que el pozo está lleno.
  • Fuga de memoria (Memory Leak) : es para solicitar la memoria, pero no liberarla, lo que resulta en una pérdida de espacio de memoria. El dicho popular es que algunas personas no cagan en el hoyo.

1. Desbordamiento de la memoria

En varias áreas de memoria de la JVM, además del contador de programa, varias otras áreas de tiempo de ejecución tienen la posibilidad de excepciones de memoria insuficiente (OOM).

Área de memoria JDK 1.8

1.1, desbordamiento del montón de Java

El montón de Java se usa para almacenar instancias de objetos. Solo necesitamos crear objetos continuamente y asegurarnos de que haya una ruta accesible entre las raíces de GC y el objeto para evitar el mecanismo de recolección de basura para borrar estos objetos. Luego, a medida que aumenta el número de objetos , la capacidad total alcanza el montón más grande. Después de que se limite la capacidad, se producirá una excepción de desbordamiento de memoria.

Veamos un ejemplo de código:

/**
 * VM参数: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 */
public class HeapOOM {
    
    
    static class OOMObject {
    
    
    }

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

A continuación, configuremos los parámetros de JVM cuando se inicie el programa. El tamaño de la memoria está limitado a 20M, no se permite la expansión y la máquina virtual vuelca la instantánea de volcado de memoria dinámica a través del parámetro -XX: + HeapDumpOnOutOf-MemoryError.

Configure los parámetros de inicio de JVM en Idea como se muestra a continuación:

Idea establece parámetros de JVM

Ejecutarlo:

Excepción de desbordamiento de memoria de montón

La excepción OutOfMemoryError de la memoria dinámica de Java es la excepción de desbordamiento de memoria más común en aplicaciones prácticas. Cuando se produce un desbordamiento de la memoria del montón de Java, la información de la pila de excepciones "java.lang.OutOfMemoryError" seguirá un mensaje adicional "Espacio de montón de Java". El archivo de instantánea del archivo de almacenamiento dinámico de Java se vuelca en el archivo java_pid18728.hprof.

Para resolver la anomalía de esta área de memoria, el método convencional es analizar primero la instantánea de volcado de pila de Dump a través de una herramienta de análisis de imágenes de memoria (como JProfiler, Eclipse Memory Analyzer, etc.).

Consulte la información sobre el uso de la memoria de la siguiente manera:

Archivo de instantánea de volcado de pila abierto por Jprofiler

Luego, puede ver los problemas de código de la siguiente manera:

Problemas con el código de vista de jprofiler

Parámetros relacionados con la JVM de pila común:

-XX:PrintFlagsInitial: Ver el valor inicial predeterminado de todos los parámetros -XX:PrintFlagsFinal: Ver el valor final de todos los parámetros (puede modificarse, ya no es el valor inicial)
-Xms: Memoria de espacio de pila inicial (el valor predeterminado es 1/64 de la memoria física)
-Xmx: Memoria de espacio de pila máxima (predeterminado 1/4 de la memoria física)
-Xmn: establezca el tamaño de la generación joven (valor inicial y valor máximo)
-XX:NewRatio: configure la proporción de la generación joven y la generación anterior en la estructura del montón
-XX:SurvivorRatio: establezca la proporción de Eden y el espacio S0 / S1 en la
-XX:MaxTenuringThresholdgeneración joven: establezca la generación joven Edad máxima de la basura (por defecto 15)
-XX:+PrintGCDetails: Salida de registro de procesamiento de GC detallado
Imprima GCinformación breve: ① -XX:+PrintGC-verbose:gc
-XX:HandlePromotionFailure: Si se debe establecer la garantía de asignación de espacio

1.2, pila de máquina virtual y desbordamiento de pila de método local

La máquina virtual HotSpot combina la pila de la máquina virtual y la pila del método local en una. Por lo tanto, para HotSpot, aunque existe el parámetro -Xoss (establecer el tamaño de la pila del método local), en realidad no tiene ningún efecto. La capacidad de la pila solo puede ser determinado por el parámetro -Xss a configurar. Con respecto a la pila de máquinas virtuales y la pila de métodos locales, hay dos excepciones:

  • Si la profundidad de pila solicitada por el subproceso es mayor que la profundidad máxima permitida por la máquina virtual, se lanzará StackOverflowErroruna excepción.

  • Si la pila de memoria de la máquina virtual permite la expansión dinámica, cuando la expansión de la capacidad no se aplica a la pila de memoria suficiente, generará OutOfMemoryErroruna excepción.

1.2.1 、 StackOverflowError

La máquina virtual HotSpot no admite la expansión dinámica de la pila. En la máquina virtual HotSpot, las dos situaciones siguientes causarán un StackOverflowError.

  • La capacidad de la pila es demasiado pequeña

    De la siguiente manera, use el parámetro Xss para reducir la capacidad de la memoria de la pila

/**
 * vm参数:-Xss128k
 */
public class JavaVMStackSOF {
    
    
    private int stackLength = 1;

    public void stackLeak() {
    
    
        stackLength++;
        stackLeak();
    }

    public static void main(String[] args) throws Throwable {
    
    
        JavaVMStackSOF oom = new JavaVMStackSOF();
        try {
    
    
            oom.stackLeak();
        } catch (Throwable e) {
    
    
            System.out.println("stack length:" + oom.stackLength);
            throw e;
        }
    }

}

resultado de la operación:

Desbordamiento de memoria de pila

  • El marco de la pila es demasiado grande

    De la siguiente manera, a través de una larga lista de variables, para ocupar el espacio de tabla de la variable local.

    carbón

resultado de la operación:

imagen-20210324211958180

Ya sea porque el marco de la pila es demasiado grande o porque la capacidad de la pila de la máquina virtual es demasiado pequeña, cuando no se puede asignar la nueva memoria del marco de la pila, la máquina virtual HotSpot arroja excepciones StackOverflowError.

1.2.2 、 OutOfMemoryError

Aunque no admite la expansión dinámica de la pila, al crear subprocesos continuamente, también se puede generar una excepción de desbordamiento de memoria en HotSpot.

Cabe señalar que no existe una relación directa entre la excepción de desbordamiento de memoria generada de esta manera y si el espacio de la pila es suficiente, y depende principalmente del estado de uso de memoria del propio sistema operativo. Debido a que el sistema operativo tiene memoria limitada para cada proceso, la cantidad de subprocesos naturalmente excederá la capacidad del proceso.

La creación de un hilo provoca una excepción de desbordamiento de memoria:

/**
 * vm参数:-Xss2M
 */
public class JavaVMStackOOM {
    
    
    private void dontStop() {
    
    
        while (true) {
    
    
        }
    }

    public void stackLeakByThread() {
    
    
        while (true) {
    
    
            Thread thread = new Thread(new Runnable() {
    
    
                public void run() {
    
    
                    dontStop();
                }
            });
            thread.start();
        }
    }

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

Lo anterior es un fragmento de código relativamente riesgoso, que puede hacer que el sistema falle. Los resultados son los siguientes:

imagen-20210324213320530

1.3, área de método y desbordamiento de grupo constante en tiempo de ejecución

La región de transición menciona un método y un grupo de constantes de tiempo de ejecución donde, después del grupo de constantes de cadena JDK1.7 al montón, JDK1.8 apartó una región de memoria en el espacio directo -dimensional para lograr la región lateral.

String: intern () es un método local. Si el grupo de constantes de cadena ya contiene una cadena igual a este objeto String, devuelve una referencia al objeto String que representa esta cadena en el grupo; de lo contrario, lo hará La cadena contenida en el String El objeto se agrega al grupo constante y se devuelve una referencia a este objeto String. En máquinas virtuales HotSpot anteriores a JDK 6 o anterior, el grupo constante se asigna en la generación permanente, y la generación permanente en sí no está limitada en memoria y pueden ocurrir errores:

java.lang.OutOfMemoryError: PermGen space

1.4. Desbordamiento de memoria directa de la máquina

El tamaño de la memoria directa (Direct Memory) se puede especificar mediante el parámetro -XX: MaxDirectMemorySize. Si no se especifica, el valor predeterminado es el mismo que el montón máximo de Java (especificado por -Xmx).

Obtenga la Unsafeinstancia directamente a través de la reflexión y solicite la asignación de memoria al sistema operativo a través de la reflexión:

/**
 * vm参数:-Xmx20M -XX:MaxDirectMemorySize=10M
 */
public class DirectMemoryOOM {
    
    
    private static final int _1MB = 1024 * 1024;

    public static void main(String[] args) throws Exception {
    
    
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
    
    
            unsafe.allocateMemory(_1MB);
        }
    }
}

resultado de la operación:

imagen-20210324215114989

Una característica obvia del desbordamiento de memoria causado por la memoria directa es que no se verán anomalías obvias en el archivo Heap Dump.

2. Pérdida de memoria

La recolección de memoria, en resumen, significa que los objetos que deberían ser recolectados como basura no son recolectados como basura.

Pérdida de memoria

En la figura anterior: El objeto X se refiere al objeto Y. El ciclo de vida de X es más largo que el ciclo de vida de Y. Cuando finaliza el ciclo de vida de Y, el recolector de basura no recuperará el objeto Y.

Veamos algunos ejemplos de fugas de memoria:

  • La clase de colección estática provoca una pérdida de memoria

    El ciclo de vida de la colección estática es el mismo que el de la JVM, por lo que los objetos a los que hace referencia la colección estática no se pueden liberar.

public class OOM {
    
    
 static List list = new ArrayList();

 public void oomTests(){
    
    
   Object obj = new Object();

   list.add(obj);
  }
}
  • Modo singleton :

    De manera similar al ejemplo anterior, el objeto singleton existirá en todo el ciclo de vida de la JVM como una variable estática después de la inicialización. Si el objeto singleton contiene una referencia externa, el GC no reclamará este objeto externo, lo que provocará una pérdida de memoria.

  • Conexiones de conexión de datos, IO, Socket, etc.

    Cuando la conexión creada ya no está en uso, es necesario llamar a la estrecha método para cerrar la conexión. Sólo después de que se cierra la conexión, el GC reclamará los objetos correspondientes (Conexión, Norma, conjunto de resultados, Sesión). Olvidar cerrar estos recursos dará como resultado una ocupación continua de la memoria, que GC no puede reclamar.

        try {
    
    
            Connection conn = null;
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("url", "", "");
            Statement stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("....");
          } catch (Exception e) {
    
     
           
          }finally {
    
    
            //不关闭连接
          }
        }
  • Alcance irrazonable de variables

    El alcance de la definición de una variable es mayor que el alcance de su uso, es probable que tenga una pérdida de memoria; o el objeto ya no se usa y el objeto no se establece en nulo en el tiempo, lo que puede causar una pérdida de memoria.

public class Simple {
    
    
    Object object;
    public void method1(){
    
    
        object = new Object();
        //...其他代码
        //由于作用域原因,method1执行完成之后,object 对象所分配的内存不会马上释放
        object = null;
    }
}
  • Clase interna no estática que hace referencia a una clase externa

    La inicialización de clases internas no estáticas (o clases anónimas) siempre depende de instancias de clases externas. De forma predeterminada, cada clase interna no estática contiene una referencia implícita a su clase contenedora . Si este objeto de clase interna se usa en el programa , no se reciclará incluso si el objeto de clase contenedor sale del alcance (el objeto de clase interna contiene implícitamente una referencia a un objeto de clase externo para que no se pueda reciclar).

  • Cambios en el valor hash

    El valor Hash del objeto cambia. Cuando se utilizan contenedores como HashMap, HashSet, etc., el valor Hah después de que se modifica el objeto es diferente del valor Hash almacenado en el contenedor, por lo que el objeto actual no se puede eliminar solo del contenedor , causando pérdidas de memoria.

  • Pérdida de memoria causada por ThreadLocal

    ThreadLocal puede lograr el aislamiento de subprocesos de las variables, pero si se usa incorrectamente, se pueden introducir pérdidas de memoria.



referencia:

[1]: "Comprensión profunda de la máquina virtual Java: funciones avanzadas y mejores prácticas de JVM" por Zhou Zhipeng

[2]: "Especificación de máquina virtual Java" traducida por Zhou Zhipeng y otros

[3]: "Principio de diseño e implementación de JVM de máquina virtual Java secreta", editado por Feng Yafei

[4]: ¿Qué son las pérdidas de memoria y las pérdidas de memoria en Java? Déjame darte un ejemplo de buen gusto

[5]: Que Xiaobai aún no ha entendido el desbordamiento de la memoria, así que solo puedo decírselo con un caso.

[6]: Artefacto de análisis de rendimiento de JProfiler integrado de Intellij IDEA

[7]: Serie JVM (dos) -área de memoria JVM en detalle

[8] 1 artículo para descubrir las razones y las soluciones para 8 tipos de JVM sin memoria (OOM)

[9]: En diez casos de desbordamiento de la memoria JVM, ¿cuántos ha encontrado?

[10]: Serie JVM (2): Pérdida de memoria JVM y desbordamiento de memoria y resolución de problemas

Supongo que te gusta

Origin blog.csdn.net/sinat_40770656/article/details/115220345
Recomendado
Clasificación