JVM: recolección de basura; algoritmo de recolección de basura (3)

Referencia antes de leer

https://blog.csdn.net/MinggeQingchun/article/details/126947384

https://blog.csdn.net/MinggeQingchun/article/details/127066302

1. Si el objeto marcado es basura

La estructura de memoria de la JVM incluye cinco áreas: contador de programa, pila de máquina virtual, pila de método local, área de almacenamiento dinámico y área de método.

Entre ellas, las tres áreas de contador de programa, pila de máquina virtual y pila de método local nacen y se destruyen con subprocesos, por lo que la asignación de memoria y la recuperación de estas áreas son deterministas y no hay necesidad de pensar demasiado en el reciclaje, porque Cuando finaliza el método o finaliza el subproceso, la memoria se recupera de forma natural.

El área de almacenamiento dinámico de Java es diferente del área de métodos. La asignación y recuperación de esta parte de la memoria es dinámica, que es a lo que debe prestar atención el recolector de elementos no utilizados.

(1) Método de conteo de referencia

El conteo de referencias fue una estrategia temprana en los recolectores de basura. En este enfoque, cada instancia de objeto en el montón tiene un recuento de referencias. Cuando se crea un objeto, la instancia del objeto se asigna a una variable cuyo recuento se establece en 1. Cuando a cualquier otra variable se le asigna una referencia a este objeto, el recuento aumenta en 1 (a = b, entonces el contador de la instancia de objeto a la que hace referencia b es +1), pero cuando la referencia de una instancia de objeto supera la duración o se establece en Cuando se obtiene un nuevo valor, el contador de referencia de la instancia del objeto se reduce en 1. Cualquier instancia de objeto con un recuento de referencias de 0 puede recolectarse como basura. Cuando una instancia de objeto se recolecta como basura, los contadores de referencia de cualquier instancia de objeto a la que hace referencia se reducen en 1

  • Ventajas: El colector de conteo de referencia se puede ejecutar muy rápidamente y entrelazado en la ejecución del programa. Es más beneficioso para el entorno en tiempo real donde el programa no necesita ser interrumpido por mucho tiempo.

  • Desventajas: No se pueden detectar las referencias circulares.  Si el objeto A tiene una referencia al objeto B, el objeto B a su vez hace referencia al objeto A. De esta manera, su cuenta de referencia nunca puede ser 0

JVM no adopta este enfoque, sino el análisis de accesibilidad

(2) Análisis de accesibilidad

El recolector de elementos no utilizados en la máquina virtual de Java utiliza el análisis de accesibilidad para explorar todos los objetos supervivientes.

El algoritmo de accesibilidad es un algoritmo utilizado por las principales máquinas virtuales en la actualidad. El programa considera todas las relaciones de referencia como un gráfico, comienza desde un nodo GC Roots, busca el nodo de referencia correspondiente y, después de encontrar este nodo, continúa buscando la referencia. nodo de este nodo Nodos de referencia, cuando se encuentran todos los nodos de referencia, los nodos restantes se consideran nodos no referenciados, es decir, nodos inútiles, y los nodos inútiles se considerarán objetos reciclables.

En el lenguaje Java, los objetos que se pueden usar como GC Roots incluyen lo siguiente:

(1) Objetos a los que se hace referencia en la pila de la máquina virtual (tabla de variables locales en el marco de la pila)

(2) Objetos referenciados por propiedades estáticas de clase en el área de método

(3) Objetos referenciados por constantes en el área de método

(4) Objetos a los que hace referencia JNI (método nativo) en la pila de métodos locales

De la figura anterior, referencia1, referencia2 y referencia3 son todos raíz de GC

referencia1 --> instancia de objeto 1

referencia2 --> instancia de objeto 2

referencia3 --> instancia de objeto 4 --> instancia de objeto 6

Se puede concluir que las instancias de objeto 1, 2, 4 y 6 tienen acceso a objetos, es decir, objetos sobrevivientes, objetos que GC no puede reciclar. Sin embargo, aunque las instancias 3 y 5 están conectadas directamente, no hay GC Roots conectado a ellas, es decir, objetos que GC Roots no puede alcanzar y que GC reciclará

2. El principio de la recolección de basura.

El principio básico de la recolección de basura GC (Garbage Collection): reciclar objetos que ya no se usan en la memoria. El método utilizado para reciclar en GC se llama recolector. Dado que GC necesita consumir algunos recursos y tiempo, Java está preocupado por la vida útil de los objetos Después de analizar las características del ciclo, los objetos se recopilan de acuerdo con la nueva generación y la generación anterior, para acortar la pausa causada por GC a la aplicación tanto como sea posible

  • La memoria del montón se divide en 两块una pieza 年轻代y la otra es老年代。老年代:年轻代比例为2:1

  • La generación joven se divide además en Edeny survivor. La proporción de su tamaño de espacio es 8:2 por defecto

  • El área de supervivencia se divide en s0(From Space y s1(To Space . Estos dos espacios son exactamente del mismo tamaño, son un par de gemelos, la proporción de ellos es 1:1

Proceso de recolección de elementos no utilizados de la memoria del montón 

1. 新生成El objeto se coloca por primera vez en Edenla zona (zona Eden ) , cuando la zona Eden 满了se activaráMinor GC

2. Los objetos que sobrevivieron al GC en el primer paso se moverán al Desde el espacio en el área S0 en survivorla zona Cuando el área S0 esté llena , se activará Los objetos que sobrevivieron al área S0 se moverán a el To Space en el área S1 , y el área S0 es libre.Minor GC

Después de que S1 esté lleno, luego GC, y los objetos sobrevivientes se mueven al área S0 nuevamente, y el área S1 está libre , por lo que el GC se repite repetidamente.Cada vez que se realiza GC, la edad del objeto alcanzará 涨一岁un cierto umbral (15), y entrará老年代

3. Después de una ocurrencia Minor GC(condición previa), la generación anterior puede aparecer Major GC, dependiendo del recolector de basura

Condiciones de disparo de GC completo

  • La llamada manual a System.gc ejecutará continuamente Full GC

  • El espacio de la generación anterior es insuficiente/lleno

  • Espacio insuficiente/lleno en el área de método

stop-the-world(STW)

stop-the-worldOcurrirá en cualquier algoritmo de GC. stop-the-world significa que la JVM detiene 停止la ejecución de la aplicación porque necesita realizar GC.

Cuando ocurre una detención del mundo, todos menos los subprocesos necesarios para GC 线程entran en 等待estado hasta que se completa la tarea de GC. La optimización de GC es a menudo para reducir la ocurrencia de stop-the-world

JVM GC solo recicla 堆内存y 方法区内objetos. Y 栈内存la JVM liberará automáticamente los datos después de que quede fuera del alcance, por lo que no está dentro del alcance de gestión de JVM GC

Puede referirse a

7 tipos de recolectores de basura jvm, todos ellos se entienden esta vez, echemos un vistazo

1, GC menor

Colección de objetos en la nueva generación.

Minor GC se refiere al GC de nueva generación, es decir, la operación de recolección de basura que ocurre en la nueva generación (incluyendo el área de Eden y el área de Survivor).Cuando la nueva generación no puede asignar espacio de memoria para nuevos objetos, Minor GC se activará. Debido a que el ciclo de vida de la mayoría de los objetos en la nueva generación es muy corto, la frecuencia de Minor GC es muy alta.Aunque activará Stop-the-world, su velocidad de reciclaje es muy rápida.

2, mayor GC

colección de objetos en la vieja generación

Major GC limpia el área Tenured, que se utiliza para reciclar la vejez Cuando ocurre Major GC, generalmente habrá al menos un Minor GC

3, GC completo

Llame activamente a System.gc() para aplicar GC en el programa

Full GC es un GC global para toda la nueva generación, la generación anterior y el metaespacio (metaspace, java8 y superior reemplazan la generación permanente perm gen). Full GC no es igual a Major GC, ni es igual a Minor GC+Major GC La ocurrencia de Full GC depende de la combinación de recolectores de basura utilizados para explicar qué tipo de recolección de basura es.

Tres o cinco referencias

1. Fuerte referencia

2. Referencias blandas

3, cita débil

4. Referencias fantasma

5. Referencia del terminador

(1) Referencia fuerte

Solo cuando todos los objetos de GC Roots no hacen referencia al objeto a través de [referencia fuerte], el objeto puede ser recolectado como basura

(2) Referencia suave (SoftReference)

Cuando solo las referencias suaves se refieren al objeto, después de la recolección de elementos no utilizados, la recolección de elementos no utilizados se activará nuevamente cuando la memoria aún sea insuficiente.El reciclaje de objetos de referencias suaves puede cooperar con la cola de referencia para liberar la referencia suave en sí.

Para el siguiente código, la VM establece la memoria de almacenamiento dinámico en 20M, y la parte del comentario informará un error de desbordamiento de memoria de almacenamiento dinámico después de ejecutar el ciclo 5 veces

/**
 * 软引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GC2SoftReference {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        //java.lang.OutOfMemoryError: Java heap space
        /*List<byte[]> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            list.add(new byte[_4MB]);
        }
        System.in.read();*/

        //软引用
        soft();
    }

    public static void soft() {
        // list --> SoftReference --> byte[]

        List<SoftReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());

        }
        System.out.println("循环结束:" + list.size());
        for (SoftReference<byte[]> ref : list) {
            System.out.println(ref.get());
        }
    }
}

Use la salida de referencia suave, los primeros 4 objetos se reciclan al verificar el valor, y solo existe el quinto objeto 

Agregar parámetros a la máquina virtual para ver el resultado

-Xmx20m -XX:+ImprimirGCDetalles -verbose:gc

GC se activa cuando se emite el tercer objeto 

cola de referencia suave

/**
 * 软引用, 配合引用队列
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GC3SoftReferenceQueue {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        List<SoftReference<byte[]>> list = new ArrayList<>();

        // 引用队列
        ReferenceQueue<byte[]> queue = new ReferenceQueue<>();

        for (int i = 0; i < 5; i++) {
            // 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去
            SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);
            System.out.println(ref.get());
            list.add(ref);
            System.out.println(list.size());
        }

        // 从队列中获取无用的 软引用对象,并移除
        Reference<? extends byte[]> poll = queue.poll();
        while( poll != null) {
            list.remove(poll);
            poll = queue.poll();
        }

        System.out.println("===========================");
        for (SoftReference<byte[]> reference : list) {
            System.out.println(reference.get());
        }

    }
}

(3) Referencia débil (Referencia débil)

Cuando solo las referencias débiles se refieren al objeto, durante la recolección de basura, independientemente de si la memoria es suficiente, el objeto de referencia débil se reciclará y la referencia débil en sí se puede liberar junto con la cola de referencia.

/**
 * 弱引用
 * -Xmx20m -XX:+PrintGCDetails -verbose:gc
 */
public class GC4WeakReference {
    private static final int _4MB = 4 * 1024 * 1024;

    public static void main(String[] args) {
        //  list --> WeakReference --> byte[]
        List<WeakReference<byte[]>> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);
            list.add(ref);
            for (WeakReference<byte[]> w : list) {
                System.out.print(w.get()+" ");
            }
            System.out.println();

        }
        System.out.println("循环结束:" + list.size());
    }
}

(4) Referencia fantasma (PhantomReference)

Debe usarse con la cola de referencia, principalmente con ByteBuffer. Cuando el objeto al que se hace referencia se recicla, la referencia fantasma se pondrá en cola y el subproceso del controlador de referencia llamará al método relacionado con la referencia fantasma para liberar la memoria directa.

(5) Referencia final (Referencia final)

Todos los objetos heredan de Object, y hay un método finalize() en Object, el objeto puede anular el método finalize(), que se llamará cuando el objeto se recopile como basura. Pero el objeto ya no tiene una referencia sólida y el método finalize() se implementa en realidad a través de una referencia de finalizador. Después de que el objeto B desconecte la referencia fuerte de A4, la referencia del finalizador se agregará a la cola de referencia, que será escaneada por un controlador de finalización con una prioridad muy baja. Cuando se escanea la referencia del finalizador en la cola de referencia, el A4 al que se hace referencia ser ejecutado El método finalize() del objeto. Dado que el método finalize() no se ejecutará de inmediato, se pondrá en cola primero y el finalizeHandler responsable del análisis tiene una prioridad baja, lo que puede causar que finalize() se retrase, por lo que no se recomienda usarlo para el reciclaje de recursos.

No hay necesidad de codificación manual, pero se usa internamente con la cola de referencia.Durante la recolección de basura, la referencia del finalizador se pone en cola (el objeto al que se hace referencia no se ha reciclado temporalmente), y luego el subproceso del Finalizador encuentra el objeto al que se hace referencia a través del finalizador. referencia y llama a su método de finalización, el objeto al que se hace referencia solo se puede recuperar durante el segundo GC

4. Algoritmo de recolección de basura

(1) Algoritmo de borrado de marcas (barrido de marcas)

Etapa de marcado:

El proceso de marcado es en realidad el proceso del algoritmo de análisis de accesibilidad presentado anteriormente, que atraviesa todos los objetos GC Roots y marca los objetos accesibles desde el objeto GCRoots, generalmente en el encabezado del objeto, registrándolo como objeto accesible.

Fase de limpieza:

El proceso de borrado consiste en atravesar la memoria del montón, y si se encuentra que un objeto no está marcado como un objeto alcanzable (al leer la información del encabezado del objeto), se reciclará

defecto:

(1) Generará fragmentación de memoria

(2) Cuando hay objetos grandes que necesitan asignar espacio de memoria continuo, el mecanismo de recolección de basura puede activarse dos veces

Conclusión: es adecuado para la generación anterior y es más eficiente cuando hay muchos objetos sobrevivientes.

(2) Algoritmo de compresión/recopilación de marcas (Mark Compact)

Después de limpiar la basura marcada, comprima el espacio de memoria disperso y copie continuamente la memoria activa y la memoria discontinua en el espacio de memoria aproximadamente continuo para asegurarse de que la memoria utilizada tenga tantos agujeros como sea posible.

Ventajas: evita mucha fragmentación de la memoria

Desventajas: menor eficiencia general

(3) Algoritmo de copia (Copia)

Marque todos los objetos sobrevivientes y copie estos objetos sobrevivientes en una nueva pieza de memoria (el espacio de memoria derecho en la figura), y luego recupere toda la memoria enviada (el espacio de memoria izquierdo en la figura)

ventaja:

(1) Alta eficiencia, sin residuos

(2) Solo escanee todo el espacio una vez

defecto:

(1) Necesita un espacio de memoria vacío

(2) Necesidad de copiar el objeto en movimiento

(3) La tasa de utilización de la memoria es baja y no es adecuada para su uso al final del año cuando la tasa de supervivencia del objeto es alta

Aplicable a la nueva generación, es decir, "vida y muerte"

5. Algoritmo de recuperación generacional

El algoritmo de recolección generacional es el algoritmo de reciclaje que utilizan actualmente las máquinas virtuales. Resuelve el problema de que la colación de marcas no funciona para la generación anterior, dividiendo la memoria en generaciones. En general, el área del montón se divide en la generación anterior (Generación Titular) y la nueva generación (Generación Joven), y hay otra generación fuera del área del montón, que es la generación permanente (Generación Permanente).

Antes de JDK8, su implementación del área de métodos se denominaba generación permanente , que utilizaba una parte del montón como área de métodos.

Después de JDK8, se eliminó la implementación de la generación permanente y se reemplazó una implementación de metaespacio.El metaespacio usaba parte del sistema operativo (algo de memoria) como área de método en lugar de ser parte del montón.

Se usan diferentes algoritmos en diferentes edades, de modo que se usa el algoritmo más adecuado.La tasa de supervivencia de la nueva generación es baja y se puede usar el algoritmo de replicación. Sin embargo, la tasa de supervivencia de los objetos en la vejez es alta y no hay espacio adicional para asignarle garantías, por lo que solo se pueden usar algoritmos de eliminación de marcas o finalización de marcas.

Los objetos se asignan primero en el área de Eden

Cuando el espacio en la nueva generación es insuficiente, se activa un gc menor, los objetos sobrevivientes de Eden y from se copian a to by copy, la edad de los objetos sobrevivientes aumenta en 1 y from to se intercambia

Minor gc activará la detención del mundo, suspenderá otros subprocesos de usuario y esperará a que finalice la recolección de elementos no utilizados antes de que el subproceso de usuario se reanude.

Cuando la vida del objeto exceda el umbral, se promoverá a la vejez, y la vida máxima es 15 (4 bits)

Cuando el espacio en la generación anterior es insuficiente, intentará activar el gc menor primero, y si aún no hay espacio suficiente, activará el gc completo y el tiempo de STW será más largo.

Establecer parámetros relacionados con VM 

parámetro significado
-Xms Tamaño inicial del almacenamiento dinámico (tamaño inicial de la memoria del almacenamiento dinámico, unidad m, g)
-Xmx o -XX:MaxHeapSize=tamaño El tamaño máximo del montón, generalmente no mayor al 80% de la memoria física
-Xmn 或 (-XX:NewSize=tamaño + -XX:MaxNewSize=tamaño) Tamaño cenozoico
-XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy Relación de área de supervivencia (dinámica)
-XX:Ratio de Supervivientes=cuenta Relación de zona de supervivencia
-XX:MaxTenuringThreshold=umbral Promover el umbral de la vejez
-XX:+ImprimirDistribución de tenencia Detalles de la promoción
-XX:+ImprimirGCDetalles -verbose:gc detalles del GC
-XX:+ScavengeBeforeFullGC Antes de FullGC MinorGC
-XX: Tamaño permanente El tamaño inicial de la memoria que no es de montón, la configuración general de la aplicación se inicializa a 200m, y el máximo de 1024m es suficiente
-XX:TamañoMáxPerm Tamaño máximo permitido de memoria que no es de montón
-XX:Ratio de Supervivientes=8 La relación de capacidad del área Eden al área Survivor en la generación joven, el valor predeterminado es 8, es decir, 8: 1
-XX:+Deshabilitar GC explícito Cierre System.gc()
-XX:+RecopilarGen0primero Ya sea para YGC primero durante FullGC, el valor predeterminado es falso
-XX:TLABWasteTargetPercent El porcentaje de TLAB en el área de eden, el valor predeterminado es 1%
-Xnoclassgc deshabilitar la recolección de basura

memoria TLAB 

El nombre completo de TLAB es Thread Local Allocation Buffer , es decir,线程本地分配缓存por el nombre, es un área de asignación de memoria dedicada a los hilos, que nace para acelerar la asignación de objetos.

Cada subproceso generará una TLAB, que es un área de trabajo exclusiva para el subproceso. La máquina virtual de Java utiliza esta área TLAB para evitar conflictos entre subprocesos múltiples y mejorar la eficiencia de la asignación de objetos.

El espacio TLAB generalmente no es demasiado grande. Cuando un objeto grande no se puede asignar en TLAB, se asignará directamente al montón

parámetro significado
-Xx:+UsarTLAB utilizar TLAB
-XX:+TAMAÑOTLAB Establecer el tamaño de TLAB
-XX:TLABRecargaFracciónResiduos Establezca y mantenga el tamaño de un solo objeto que ingresa al espacio TLAB, que es un valor proporcional, el valor predeterminado es 64, es decir, si el objeto es más grande que 1/64 del espacio completo, se creará en el montón
-XX:+ImprimirTLAB Ver información TLAB
Xx: Redimensionar TLAB Umbral TLABRefillWasteFraction autoajustable
/**
 * 分代回收
 * -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
 */
public class GC5Generational {
    private static final int _512KB = 512 * 1024;
    private static final int _1MB = 1024 * 1024;
    private static final int _6MB = 6 * 1024 * 1024;
    private static final int _7MB = 7 * 1024 * 1024;
    private static final int _8MB = 8 * 1024 * 1024;

    // -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc -XX:-ScavengeBeforeFullGC
    public static void main(String[] args) throws InterruptedException {

    }
}

Jardín del Edén 

El objeto Eden se transfiere a los espacios From y to, y los espacios From y to se intercambian

Los objetos grandes se promocionan directamente a la generación anterior.

OM

Supongo que te gusta

Origin blog.csdn.net/MinggeQingchun/article/details/127089533
Recomendado
Clasificación