Parte 1 de JVM_16_Conceptos relacionados con la recolección de basura_Silicon Valley

1 Comprensión de System.gc()

Comprensión del método Systen.gc()

  • De forma predeterminada, la llamada de System.gc() o Runtime.getRuntime().gc() activará explícitamente Full GC, reciclará la generación anterior y la nueva generación al mismo tiempo, e intentará liberar la memoria ocupada por la descartada. objeto.
  • Sin embargo, la llamada System.gc() viene con una vida útil de exención, que no garantiza una llamada al recolector de elementos no utilizados.
  • Los implementadores de JVM pueden determinar el comportamiento de GC de la JVM a través de la llamada System.gc(). En circunstancias normales, la recolección de elementos no utilizados debe realizarse automáticamente sin activación manual, de lo contrario sería demasiado problemático . En algunos casos especiales, como cuando estamos escribiendo un punto de referencia de rendimiento, podemos llamar a System.gc() antes de ejecutarlo.

demo01Sistema de prueba01()

public class SystemGCTest {
    
    
    public static void main(String[] args) {
    
    
        new SystemGCTest();
        // 提醒jvm的来及回收器执行gc,但是不确定是否马上执行
        System.gc();

        // 强制调用失去引用对象的finalize()
//        System.runFinalization();
    }

    @Override
    protected void finalize() throws Throwable {
    
    
        super.finalize();
        System.out.println("SystemGCTest 重写了finalize()");
    }
}

recuperación de prueba demo02

public class LocalVarGC {
    
    
    /**
     * 不会回收
     */
    public void localvarGC1() {
    
    
        byte[] buffer = new byte[10 * 1024 * 1024];
        System.gc();
    }


    /**
     * 会回收
     */
    public void localvarGC2() {
    
    
        byte[] buffer = new byte[10 * 1024 * 1024];
        buffer = null;
        System.gc();
    }

    /**
     * 不会回收
     * 局部变量表中的slot未被覆盖
     *
     */
    public void localvarGC3() {
    
    
        {
    
    
            byte[] buffer = new byte[10 * 1024 * 1024];
        }
        System.gc();
    }

    /**
     * 可以回收
     * value复用了buffer的槽,可以被回收
     */
    public void localvarGC4() {
    
    
        {
    
    
            byte[] buffer = new byte[10 * 1024 * 1024];
        }
        int value = 10;
        System.gc();
    }

    /**
     * 可以回收
     */
    public void localvarGC5() {
    
    
        localvarGC1();
        System.gc();
    }

    public static void main(String[] args) {
    
    
        LocalVarGC localVarGC = new LocalVarGC();
        localVarGC.localvarGC5();
    }
}

2 Desbordamiento de memoria y pérdida de memoria

Sin memoria (OOM)

  • En comparación con las fugas de memoria, el desbordamiento de memoria es lo más simple posible de entender, pero de manera similar, el desbordamiento de memoria también es uno de los culpables de que el programa se cuelgue.

  • Dado que GC se ha estado desarrollando, en general, a menos que la memoria ocupada por la aplicación crezca muy rápido, lo que hace que la recolección de elementos no utilizados se mantenga al ritmo del consumo de memoria, no es fácil que ocurra OOM.

  • En la mayoría de los casos, el GC realizará la recolección de basura de varios grupos de edad. Si realmente no es suficiente, se ampliará y se realizará una operación exclusiva de GC completo. En este momento, se recuperará una gran cantidad de memoria para continuar uso por la aplicación.

  • La explicación de OutOfMemoryError en el javadoc es que no hay memoria libre y el recolector de elementos no utilizados no puede proporcionar más memoria .

  • En primer lugar, hablemos de la situación en la que no hay memoria libre: significa que la memoria de almacenamiento dinámico de la máquina virtual Java no es suficiente. Hay dos razones:

(1) La configuración de memoria de montón de la máquina virtual Java no es suficiente.

Por ejemplo: puede haber un problema de pérdida de memoria; también es muy probable que el tamaño del montón no sea razonable. Por ejemplo, tenemos que tratar con una cantidad de datos relativamente objetiva, pero el tamaño del montón de JVM no se especifica explícitamente o el valor especificado es demasiado pequeño. Podemos ajustarlo por parámetros -Xmx, Xmx.

(2) Se crea una gran cantidad de objetos grandes en el código y el recolector de basura no puede recopilarlos durante mucho tiempo (hay referencias)

Para la versión anterior de Oracle JDK, debido a que el tamaño de la generación permanente es limitado y JVMU está muy inactivo para la recolección de elementos no utilizados de generación permanente (como el reciclaje constante de grupos, la escritura en tipos que ya no son necesarios), por lo que cuando continuamos con agregar nuevos tipos de veces, OutOfMemoryError en la generación permanente también es muy común, especialmente cuando hay una gran cantidad de tipos dinámicos generados en tiempo de ejecución; cachés de cadenas internas similares ocupan demasiado espacio, lo que también puede causar problemas de OOM. La información de excepción correspondiente se marcará y relacionará con la generación permanente: " java.lang.OutOfMemoryError: PermGen space ".

Con la introducción del área de metadatos, ya no es tan vergonzoso que el método vaya a la memoria, por lo que el OOM correspondiente ha cambiado. Cuando ocurre OOM, la información de excepción se convierte en: " java.lang.OutOfMemoryError: Metaspace ". La memoria directa insuficiente también causará OOM

  • Hay un significado implícito de que entre el lanzamiento de OutOfMemoryError, el recolector de basura generalmente se activará y hará todo lo posible para limpiar el espacio.
    • Por ejemplo: en el análisis del mecanismo de referencia, implica que la JVM intentará reciclar el objeto al que apunta la referencia blanda.
    • En el método java.nio.Bits.reserveMemory(), podemos ver claramente que se llamará a System.gc() para limpiar el espacio.
  • Por supuesto, no en todos los casos se activará el recolector de basura
    • Por ejemplo, si asignamos un objeto súper grande, similar a un dato súper grande que excede el valor máximo del montón, la JVM puede juzgar que la recolección de basura no puede resolver este problema, por lo que arroja directamente OutOfMemoryError.

Pérdida de memoria

También conocido como "fuga de almacenamiento". Estrictamente hablando, solo cuando el programa ya no utiliza los objetos, pero el GC no puede recuperarlos, se denomina pérdida de memoria.

Pero en la situación real, a veces un mal momento (o negligencia) hará que la vida útil del objeto se alargue mucho o incluso cause OOM, que también se puede llamar "pérdida de memoria" en un sentido amplio.

En la medida de lo posible, las fugas de memoria no harán que el programa se acelere de inmediato, pero una vez que se produzca una fuga de memoria, la memoria disponible en el programa se erosionará gradualmente, agotando directamente la memoria y, finalmente, se producirá una excepción OutOfMemory, lo que hará que programa para fallar.

Tenga en cuenta que el espacio de almacenamiento aquí no es el valor de la memoria física, sino el tamaño de la memoria virtual, que depende del tamaño establecido en el área de intercambio de disco.

imagen-20220904230645166

Ejemplo:

1. Modo único

El ciclo de vida de un singleton es tan largo como el de una aplicación, por lo que si se mantiene una referencia a un objeto externo en un programa singleton, el objeto externo no se puede reciclar, lo que conducirá a la prosperidad de las fugas de memoria.

2. Algunos recursos que proporcionan cerrar no están cerrados, lo que provoca pérdidas de memoria

La conexión de la base de datos (dataSource.getConnection()), la conexión de red (socket) y la conexión io deben cerrarse manualmente; de ​​lo contrario, no se pueden reciclar.

3 Detener el mundo

Detener el mundo Descripción

  • Detener el mundo, o STW para abreviar, se refiere a la pausa de la aplicación durante la ocurrencia del tiempo GC. Cuando ocurre una pausa, todo el hilo de la aplicación se suspenderá sin ninguna respuesta , un poco como una sensación de estancamiento, esta pausa se llama STW.

    • Enumerar los nodos raíz (GC Roots) en el algoritmo de análisis de accesibilidad hará que todos los subprocesos de ejecución de Java se detengan.
      • El trabajo de análisis debe realizarse en una instantánea coherente
      • Coherencia significa que todo el sistema de ejecución parece estar congelado en un momento determinado durante todo el análisis.
      • Si la relación de referencia del objeto sigue cambiando durante el proceso de análisis, no se puede garantizar la precisión de los resultados del análisis.
  • El subproceso de la aplicación interrumpido por STW se reanudará después de completar el GC. La interrupción frecuente hará que el usuario se sienta como una película atascada debido a la baja velocidad de la red, por lo que debemos reducir la aparición de STW.

  • El evento STW no tiene nada que ver con el GC que se usa, y todos los GC tienen este tiempo.

  • Incluso G1 no puede evitar por completo la situación de detener el mundo. Solo se puede decir que el recolector de basura está mejorando cada vez más, y la eficiencia de reciclaje es cada vez mayor, y el tiempo de pausa se acorta tanto como sea posible.

  • STW es iniciado y completado automáticamente por la JVM en segundo plano . Cuando el usuario no está visible, todos los subprocesos de trabajo normales del usuario se detienen.

  • No use System.gc() durante el desarrollo, hará que suceda Stop-the-world.

Prueba de demostración de STW

public class StopTheWorldDemo {
    
    
  public static class WorkThread extends Thread{
    
    
    List<byte[]> list = new ArrayList<byte[]>();
    @Override
    public void run() {
    
    
      try {
    
    
        while (true) {
    
    
          for (int i = 0; i < 1000; i++) {
    
    
            byte[] buffer = new byte[1024];
            list.add(buffer);
          }
          if (list.size() > 1000) {
    
    
            list.clear();
            System.gc();
          }
        }

      } catch (Exception e) {
    
    
        e.printStackTrace();
      }
    }
  }

  public static class PrintThread extends Thread {
    
    
    public final long startTime = System.currentTimeMillis();
    public int i = 0;

    @Override
    public void run() {
    
    
      try {
    
    
        while (true) {
    
    
          // 每秒打印时间信息
          long t = System.currentTimeMillis() - startTime;
          System.out.println("i = " + (i++) + ",耗时:" + t );
          Thread.sleep(1000);
        }
      } catch (Exception e) {
    
    
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) {
    
    
    WorkThread w = new WorkThread();
    PrintThread p = new PrintThread();
    w.start();
    p.start();
  }
}

usar hilo de impresión solo

i = 0,耗时:0
i = 1,耗时:1010
i = 2,耗时:2019
i = 3,耗时:3025
i = 4,耗时:4037
i = 5,耗时:5047
i = 6,耗时:6057
i = 7,耗时:7065
i = 8,耗时:8079
i = 9,耗时:9079
i = 10,耗时:10085

Cuando los dos subprocesos se inician juntos, se imprime el tiempo (el efecto no es obvio, debe cambiar la prueba)

i = 0,耗时:0
i = 1,耗时:1015
i = 2,耗时:2023
i = 3,耗时:3027
i = 4,耗时:4036
i = 5,耗时:5049
i = 6,耗时:6060
i = 7,耗时:7068
i = 8,耗时:8073
i = 9,耗时:9079
i = 10,耗时:10087

4 Paralelismo y Concurrencia de Recolección de Basura

Concurrente

  • En el sistema operativo, significa que varios programas se ejecutan y ejecutan hasta su finalización en un período de tiempo , y todos estos programas se ejecutan en el mismo procesador.
  • La concurrencia no es una "operación simultánea" en el verdadero sentido, pero la CPU divide un período de tiempo en varios períodos de tiempo (intervalos de tiempo) y luego alterna entre estos intervalos de tiempo. Dado que la velocidad de procesamiento de la CPU es muy rápida, siempre y cuando como el tiempo Si el intervalo se maneja correctamente, el usuario puede sentir que varias aplicaciones se están ejecutando al mismo tiempo.

Paralelo

  • Cuando el sistema tiene más de una CPU, cuando una CPU ejecuta un proceso, otra CPU puede ejecutar otro proceso, y los dos procesos no aprovechan los recursos de la CPU entre sí, y pueden realizarse al mismo tiempo, lo que llamamos paralelo ( Paralelo).
  • De hecho, el factor que determina el paralelismo no es la cantidad de CPU, sino la cantidad de núcleos de la CPU.Por ejemplo, también se pueden paralelizar varios núcleos de una CPU.
  • Es adecuado para escenarios de interacción débil, como la informática científica y el procesamiento en segundo plano.

Concurrencia vs Paralelo

La concurrencia se refiere a múltiples cosas que suceden al mismo tiempo al mismo tiempo.

Paralelo significa que suceden varias cosas al mismo tiempo al mismo tiempo.

Múltiples tareas simultáneas se apropian de los recursos entre sí. Múltiples tareas paralelas no se apropian de los recursos entre sí.

El paralelismo ocurre solo en el caso de múltiples CPU o una CPU con múltiples núcleos. De lo contrario, las cosas que parecen suceder al mismo tiempo en realidad se ejecutan al mismo tiempo.

Concurrencia y paralelismo en la recolección de basura

La concurrencia y el paralelismo, en el contexto de la discusión de los recolectores de basura, se pueden explicar de la siguiente manera:

  • Paralelo (Parallel) se refiere a varios subprocesos de recolección de elementos no utilizados que funcionan en paralelo, pero en este momento el subproceso del usuario todavía está en estado de espera.
    • Como ParNew, Parallel Scavenge, Parallel Old;
  • De serie
    • En comparación con el concepto de paralelismo, la ejecución de un solo subproceso
    • Si la memoria no es suficiente, el programa se suspende y el recolector de basura JVM se inicia para la recolección de basura. Después de reciclar, vuelva a iniciar el hilo del programa.

La concurrencia y el paralelismo, en el contexto de la discusión de los recolectores de basura, se pueden interpretar de la siguiente manera:

  • Concurrente: se refiere a la ejecución simultánea del subproceso de usuario y el subproceso de recolección de basura (pero no necesariamente en paralelo, pueden ejecutarse alternativamente), y el subproceso de recolección de basura no detendrá la ejecución del programa de usuario durante la ejecución.
    • El programa de usuario continúa ejecutándose, mientras que el recolector de elementos no utilizados se ejecuta en otra CPU;
    • Tales como: CMS, G1

5 Puntos seguros y áreas seguras

punto seguro

Cuando se ejecuta el programa, no es posible detener e iniciar GC en todos los lugares. Solo en posiciones específicas puede detener e iniciar GC. Estas posiciones se denominan "puntos seguros (Safepoint)".

La elección del punto seguro es muy importante, si es demasiado pequeño, puede hacer que el GC espere demasiado, si es demasiado frecuente, puede causar problemas de rendimiento en tiempo de ejecución . El tiempo de ejecución de la mayoría de las ejecuciones es muy corto, generalmente basado en " si tiene las características que permiten que el programa se ejecute durante mucho tiempo ", como llamadas a métodos, saltos de bucle y saltos de excepción .

¿Cómo verificar que todos los subprocesos se ejecutan en el punto seguro más cercano y se detienen cuando ocurre GC?

  • Interrupción preventiva: (actualmente no se utiliza ninguna máquina virtual)

    Es preferible interrumpir todos los hilos. Si todavía hay subprocesos que no están en el punto seguro, restaure el subproceso y deje que se ejecute hasta el punto seguro.

  • Interrupción activa:

    Establezca un indicador de interrupción. Cuando cada subproceso se ejecuta en el punto seguro, sondeará activamente este indicador. Si el indicador de interrupción es verdadero, se interrumpirá a sí mismo y lo suspenderá.

Región segura

El mecanismo Safepoint asegura que cuando se ejecuta el programa, encontrará un Safepoint que puede ingresar al GC en un corto período de tiempo. Pero, ¿qué pasa cuando el programa "no se ejecuta"? Por ejemplo, el subproceso está en estado de suspensión o estado bloqueado. En este momento, el subproceso no puede responder a la solicitud de interrupción de la JVM y "camina" al área segura para interrumpir y suspender. Es poco probable que la JVM espere a que el subproceso termine. estar despierto En este caso se necesita una región segura (Safe Region) para solucionarlo.

Un área segura significa que en un segmento de código, la relación de referencia de los objetos no cambiará y es seguro iniciar GC en cualquier lugar de esta área. También podemos considerar Safe Region como un SafePoint ampliado.

Cuando realmente se ejecuta:

  1. Cuando el subproceso se ejecuta en el código de la región segura, primero marca que ha llegado a la región segura.Si se produce GC durante este período, la JVM lo marcará como un subproceso en el estado de región segura;
  2. Cuando el hilo esté a punto de salir de la Región Segura, comprobará si la JVM ha completado el GC, si se completa, continuará ejecutándose, de lo contrario, el hilo debe esperar hasta que reciba una señal de que puede salir de forma segura de la Zona Segura. Región;

6 Hablando de referencias de nuevo: referencias fuertes

Esperamos describir una clase de objetos de este tipo: cuando el espacio de memoria es suficiente, se pueden mantener en la memoria; si el espacio de memoria sigue siendo reducido después de la recolección de elementos no utilizados, estos objetos se pueden descartar.

[Preguntas de la entrevista que son tanto parciales como muy frecuentes] ¿Cuál es la diferencia entre referencias fuertes, referencias blandas, referencias débiles y referencias fantasma? ¿Cuál es el escenario de uso específico?

Después de la versión JDK1.2, Java amplió el concepto de referencias y dividió las referencias en cuatro tipos: referencia fuerte, referencia suave, referencia débil y referencia fantasma.Estas cuatro fortalezas de citación se debilitaron gradualmente a su vez .

A excepción de las referencias sólidas, las otras tres referencias se pueden encontrar en el paquete java.lang.ref. La siguiente figura muestra los tipos correspondientes a estos tres tipos de referencia, y los desarrolladores pueden usarlos directamente en la aplicación.

imagen-20220912003201532

Solo la referencia del finalizador en la subclase Reference es visible en el paquete, y los otros tres tipos de referencia son públicos y se pueden usar directamente en la aplicación.

  • Referencia fuerte : la definición más tradicional de "referencia" se refiere a la asignación de referencia que comúnmente existe en el código del programa, es decir, una relación de referencia como "Objeto obj = nuevo Objeto ()". En cualquier caso, mientras exista una fuerte relación de referencia, el recolector de elementos no utilizados nunca reciclará el objeto al que se hace referencia.
  • Referencia blanda : antes de que el sistema esté a punto de desbordar la memoria que se puede enviar, estos objetos se incluirán en el alcance del reciclaje para el segundo reciclaje. Si no hay suficiente memoria después de este reciclaje, se generará una excepción de desbordamiento de memoria.
  • Referencias débiles : los objetos asociados con referencias débiles solo pueden sobrevivir hasta la próxima recolección de elementos no utilizados. Cuando el recolector de elementos no utilizados funciona, independientemente de si hay suficiente espacio en la memoria, se recuperarán los objetos asociados con referencias débiles.
  • Referencia fantasma : el hecho de que un objeto tenga una referencia virtual no afectará en absoluto su vida útil, y es imposible obtener una instancia de un objeto a través de una referencia virtual. El único propósito de establecer una asociación de referencia fantasma para un objeto es recibir una notificación del sistema cuando se recopila el objeto.

Strong Reference (referencia fuerte) no se recicla

En los programas Java, el tipo de referencia más común es la referencia fuerte (más del 99% de los sistemas ordinarios son referencias fuertes), que es nuestra referencia de objeto común más común y también es el tipo de referencia predeterminado.

Cuando se usa el operador new en el lenguaje Java para crear un nuevo objeto y asignarlo a una variable, la variable se denomina referencia fuerte al objeto.

Los objetos fuertemente referenciados son accesibles y el recolector de elementos no utilizados nunca reclamará el objeto al que se hace referencia.

Para un objeto ordinario, si no hay otra relación de referencia, siempre que exceda el alcance de la referencia o la asignación de referencia (fuerte) correspondiente sea nula, se puede recolectar como basura. Por supuesto, el tiempo de recuperación específico depende de la estrategia de recolección de basura. .

Por el contrario, los objetos de referencias suaves, referencias débiles y referencias fantasmas son suaves, débiles y virtualmente táctiles, y se pueden reciclar bajo ciertas condiciones. Por lo tanto, las referencias sólidas son una de las principales causas de las fugas de memoria de Java.

  • Una referencia fuerte puede acceder directamente al objeto de destino.
  • El objeto al que apunta la referencia fuerte no será reclamado por el sistema en ningún momento. La máquina virtual preferiría lanzar una excepción OOM que reclamar el objeto al que apunta la referencia fuerte.
  • Las referencias sólidas pueden provocar pérdidas de memoria

7 Hablando de citas de nuevo: citas blandas

Soft Reference (Referencia suave) se recicla cuando la memoria es insuficiente

Las referencias blandas se utilizan para describir objetos que son útiles pero no necesarios. Los objetos que solo están asociados con referencias suaves se incluirán en el rango de recuperación para la segunda recuperación antes de que el sistema experimente una excepción de desbordamiento de memoria. Si no hay suficiente memoria para esta recuperación, se generará un desbordamiento de memoria anormal .

Las referencias suaves a menudo se usan para implementar cachés sensibles a la memoria. Por ejemplo: la caché vuelve a utilizar referencias blandas. Si todavía hay memoria libre, puede reservar temporalmente la memoria caché y limpiar cuando la memoria sea insuficiente, para asegurarse de que los colegas que usan la memoria caché no se queden sin memoria.

Cuando el recolector de elementos no utilizados decide reclamar un objeto de fácil acceso en un punto determinado, limpiará la referencia suave y, opcionalmente, almacenará la referencia en una cola (Cola de referencia).

Similar a las referencias débiles, excepto que la máquina virtual Java intenta mantener vivas las referencias suaves durante un período de tiempo más largo y las limpia como último recurso.

La clase java.lang.ref.SoftReference se proporciona después de JDK1.2 para implementar referencias suaves.

Object obj = new Object() // 声明强引用
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 取消强引用

código de referencia suave

public class SoftReferenceTest {
    
    
  public static void main(String[] args) {
    
    
    // 创建对象,建立软引用
//    SoftReference<User> userSoftReference = new SoftReference<>(new User((1), "test"));
    // 上面的一行代码,等价于如下的三行代码
    User user = new User(1, "test");
    SoftReference<User> userSoftReference = new SoftReference<>(user);
    user = null;

    // 从软引用中重新获得强引用对象
    System.out.println(userSoftReference.get());

    System.gc();
    System.out.println("After GC:");
    // 垃圾回收之后获得软引用中的对象
    System.out.println(userSoftReference.get());

    try {
    
    
      byte[] b = new byte[1024 * 6848 - 564 * 1024]; // 根据实际老年代的大小填写
    } catch (Throwable e) {
    
    
      e.printStackTrace();
    } finally {
    
    
      System.out.println(userSoftReference.get());
    }
  }
}
class User{
    
    
  private Integer id;
  private String name;

  public User(Integer id, String name) {
    
    
    this.id = id;
    this.name = name;
  }
}

resultado de la operación:

User@16d3586
[Full GC (System.gc()) [Tenured: 0K->569K(6848K), 0.0016977 secs] 1101K->569K(9920K), [Metaspace: 144K->144K(4480K)], 0.0017364 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
After GC:
User@16d3586
[GC (Allocation Failure) [DefNew: 110K->0K(3072K), 0.0002125 secs][Tenured: 569K->569K(6848K), 0.0006989 secs] 679K->569K(9920K), [Metaspace: 144K->144K(4480K)], 0.0009355 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Allocation Failure) [Tenured: 569K->550K(6848K), 0.0006424 secs] 569K->550K(9920K), [Metaspace: 144K->144K(4480K)], 0.0006530 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
null
Heap
 def new generation   total 3072K, used 193K [0x05000000, 0x05350000, 0x05350000)
  eden space 2752K,   7% used [0x05000000, 0x050304e0, 0x052b0000)
  from space 320K,   0% used [0x05300000, 0x05300000, 0x05350000)
  to   space 320K,   0% used [0x052b0000, 0x052b0000, 0x05300000)
 tenured generation   total 6848K, used 6834K [0x05350000, 0x05a00000, 0x05a00000)
   the space 6848K,  99% used [0x05350000, 0x059fcbd8, 0x059fcc00, 0x05a00000)
 Metaspace       used 156K, capacity 2280K, committed 2368K, reserved 4480K

Cambia el tamaño ocupado a: 1024 * 6848 - 570 * 1024

User@16d3586
[Full GC (System.gc()) [Tenured: 0K->548K(6848K), 0.0014516 secs] 1030K->548K(9920K), [Metaspace: 144K->144K(4480K)], 0.0014876 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
After GC:
User@16d3586
User@16d3586
Heap
 def new generation   total 3072K, used 154K [0x05200000, 0x05550000, 0x05550000)
  eden space 2752K,   5% used [0x05200000, 0x05226820, 0x054b0000)
  from space 320K,   0% used [0x054b0000, 0x054b0000, 0x05500000)
  to   space 320K,   0% used [0x05500000, 0x05500000, 0x05550000)
 tenured generation   total 6848K, used 6833K [0x05550000, 0x05c00000, 0x05c00000)
   the space 6848K,  99% used [0x05550000, 0x05bfc708, 0x05bfc800, 0x05c00000)
 Metaspace       used 156K, capacity 2280K, committed 2368K, reserved 4480K

Si se encuentra, se puede generar la dirección del usuario al que se hace referencia suave, lo que indica que el objeto al que se hace referencia suave no se ha reciclado.

8 Hablando de referencias de nuevo: referencias débiles

Referencia débil (referencia débil) - descubrimiento y reciclaje

Las referencias débiles también se usan para describir esos objetos no esenciales, y los objetos que solo están asociados con referencias débiles solo pueden sobrevivir hasta que ocurra la siguiente recolección de elementos no utilizados . Durante el GC del sistema, siempre que se encuentren referencias débiles, los objetos que solo están asociados con aplicaciones débiles se reciclarán independientemente de si el espacio de almacenamiento dinámico del sistema es suficiente.

Sin embargo, dado que los subprocesos del recolector de elementos no utilizados suelen tener una prioridad muy baja, es posible que no se encuentren rápidamente los objetos que contienen aplicaciones débiles. En este caso, los objetos con referencias débiles pueden existir durante un período de tiempo más largo .

Las referencias débiles son lo mismo que las referencias blandas. Al construir referencias débiles, también puede especificar una cola de referencia. Cuando se recicla el objeto de referencia débil, se agregará a la cola de referencia especificada. A través de esta cola, la recuperación del objeto puede ser rastreado

Las referencias blandas y las importaciones débiles son muy adecuadas para almacenar datos de caché prescindibles . Si hace esto, cuando la memoria del sistema sea insuficiente, los datos almacenados en caché se reciclarán sin causar un desbordamiento de la memoria. Y cuando los recursos de memoria son suficientes, estos datos almacenados en caché pueden existir durante mucho tiempo, lo que acelera el sistema.

La clase java.lang.ref.WeakReference se proporciona después de JDK1.2 para implementar referencias débiles.

Object obj = new Object(); // 生命强引用
WeakReference<Object> wr = new WeaReference<ObjecL>(obj);
obj = null; // 销毁强引用

La mayor diferencia entre un objeto de referencia débil y un objeto de referencia suave es que cuando el GC está reciclando, necesita verificar si el objeto de referencia suave se recicla a través de un algoritmo, pero para el objeto de referencia débil, el GC siempre recicla. Los objetos con referencias débiles son más fáciles y rápidos de recuperar para GC.

Pregunta de la entrevista: ¿Alguna vez ha usado WeakHashMap en su desarrollo?

Ejemplo de código de referencia débil:

public class WeakReferenceTest {
    
    
  public static void main(String[] args) {
    
    
    // 构造了弱引用
    WeakReference<Object> weakReference = new WeakReference<>(new Object());
    // 从弱引用中重新获取对象
    System.out.println(weakReference.get());

    System.gc();
    // 不管当前内存空间足够与否,都会回收它的内存
    System.out.println("After GC:");
    System.out.println(weakReference.get());
  }
}

resultado:

java.lang.Object@16d3586
After GC:
null

9 Citas revisadas: Citas fantasma

Referencia fantasma: seguimiento de reciclaje de objetos

Referencia fantasma: seguimiento de reciclaje de objetos

También conocido como "referencia fantasma" o "referencia fantasma", es el más débil de todos los tipos de referencia.

Que un objeto tenga referencias virtuales no determina en absoluto el ciclo de vida del objeto. Si un objeto contiene solo referencias fantasma, es casi lo mismo que no tener referencias, y el recolector de elementos no utilizados puede reclamarlo en cualquier momento.

No puede usarse solo, ni puede obtener el objeto referenciado a través de una referencia fantasma. Al intentar obtener el objeto a través del método get() de la referencia fantasma, siempre es nulo.

El único propósito de establecer una asociación fantasma para un objeto es rastrear el proceso de recolección de elementos no utilizados. Por ejemplo: puede recibir una notificación del sistema cuando el recolector recicle este objeto.

  • Las referencias fantasma deben usarse con colas de referencia. Una referencia fantasma debe proporcionar una cola de referencia como parámetro cuando se crea. Cuando el recolector de elementos no utilizados está a punto de reclamar un objeto, si descubre que todavía tiene una referencia virtual, agregará la referencia virtual a la cola de referencia después de reciclar el objeto para notificar a la aplicación del reciclaje del objeto.
  • Dado que las referencias fantasma pueden rastrear el tiempo de recuperación de los objetos, algunas operaciones de liberación de recursos también se pueden ejecutar y registrar en referencias fantasma.
  • Después de la versión JDK1.2, se proporciona la clase PhatomReference para realizar la referencia fantasma.
Object obj = new Object();
ReferenceQueue phantomQueue = new ReferenceQueue();
PhantomReference<Object> pf = new PhantomReference<Objec>(obj, phantomQueue);
obj = null;

10 Referencias revisadas: Referencias del finalizador

Referencia final

  • Se utiliza para implementar el método finalize() del objeto, que también se puede denominar referencia del finalizador.
  • No se requiere codificación manual, su memoria se utiliza con colas de referencia.
  • En el momento del GC, las referencias del finalizador se ponen en cola. El subproceso del finalizador encuentra el objeto al que se hace referencia a través de la referencia del finalizador y llama a su método finalize(), y el objeto al que se hace referencia solo se puede reciclar durante el segundo GC.

Supongo que te gusta

Origin blog.csdn.net/weixin_43811294/article/details/126824004
Recomendado
Clasificación