Tipos de referencia en JAVA, referencia fuerte referencia blanda referencia débil referencia virtual

fuerte referencia

Ambas son referencias sólidas. Las referencias sólidas se utilizan con más frecuencia en el desarrollo. Siempre que haya referencias a objetos, GC no los reciclará.

// 我们自己定义的实体通过直接new的形式都是强引用
Object obj = new Object();

referencia suave

Los objetos generales no se reciclarán. Cuando la memoria de la aplicación esté a punto de agotarse -> cuando OOM esté a punto de ocurrir, el procesador de elementos no utilizados lo reciclará, lo que puede evitar que la aplicación esté inactiva y no disponible debido a OOM.

SoftReference<String> ref = new SoftReference<String>("aaa");

cita débil

Cuando la referencia raíz deja de ser válida, se reciclará cuando se active el GC la próxima vez. Incluso si la referencia raíz se reasigna después del reciclaje, la referencia no se restablecerá

En el proceso de exploración del área de memoria bajo su jurisdicción por parte del subproceso del recolector de basura, una vez que se encuentra un objeto con solo referencias débiles, su memoria se recuperará independientemente de si el espacio de memoria actual es suficiente o no.

WeakReference<String> ref = new WeakReference<String>("aaa");
示例
String str=new String("333");
WeakReference<String>ref=newWeakReference<>(str);
System.out.println(ref.get());
str=null;
System.out.println(ref.get());
System.gc(); // 手动触发一次gc
System.out.println(ref.get());

Resultado: 333 333 nulo

referencia ficticia

Las referencias virtuales no son referencias reales y no afectan la recolección de basura normal.Cuando
el recolector de basura decide reciclar el objeto PhantomReference, lo insertará en ReferenceQueue.

PhantomReference.class

public class PhantomReference<T> extends Reference<T> {
    
    
    public T get() {
    
    
        return null;
    }
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
    
    
        super(referent, q);
    }
}

Para un objeto, esta referencia es inútil, y es lo mismo que no tenerla. Las referencias fantasma deben usarse junto con la cola de referencia. Su función es rastrear el proceso de recolección de basura y recibir una notificación del sistema cuando el recolector reclama el objeto. Cuando el recolector de elementos no utilizados está a punto de reclamar un objeto, si descubre que todavía tiene una referencia virtual, agregará esta referencia virtual a la cola de referencia después de la recolección de elementos no utilizados y no destruirá completamente el objeto hasta que su referencia virtual asociada se elimine de la cola. .

Las referencias virtuales a menudo se ReferenceQueueusan junto con las colas de referencia.

cola de referencia

Cola de referencia

ReferenceQueue se puede usar con SoftReference/WeakReference/PhantomReference

refrenceQueueEn esencia, es una lista enlazada que garantiza synchronizedla seguridad de los subprocesos, mantiene volatileel objeto modificado head, representa el objeto principal actual y mantiene el número actual de colas.

Eche un vistazo a cómo se garantizan los problemas de seguridad de subprocesos

static private class Lock {
    
     };
private Lock lock = new Lock();

Se puede ver que aquí se usa un objeto de bloqueo global. Este es un objeto vacío, que existe solo como un objeto de bloqueo. Su núcleo es solo para bloquear removemétodos y enqueuemétodos (unirse a la cola y eliminar la cola)

synchronized(lock){
    
    
	// ...
}

Cuando llamamos removea un método, el método se llamará para Object.waitbloquear, y cuando gc se active, enqueuese llamará a notificar a todos para notificar al subproceso, que no es una operación cas, por lo que tiene poco impacto en el rendimiento.

Lo siguiente simula un escenario: cuando gc recicla nuestro objeto, la clave de referencia en el mapa se elimina.

public class Test {
    
    

    private static ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
    private static int _1M = 1024 * 1024;

    public static void main(String[] args) throws InterruptedException {
    
    
        final Map<Object, MybObject> map = new HashMap<>();
        Thread thread = new Thread(() -> {
    
    
            try {
    
    
                int n = 0;
                MybObject k;
                while (null != (k = (MybObject) referenceQueue.remove())) {
    
    
                    // 每次触发gc都会进来一次
                    System.out.println("gc:" + (++n));
                    map.remove(k.key);
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        thread.setDaemon(true);
        thread.start();
				// 创建1000个对象放入map中
        for (int i = 0; i < 1000; i++) {
    
    
            byte[] bytesKey = new byte[_1M];
            byte[] bytesValue = new byte[_1M];
            map.put(bytesKey, new MybObject(bytesKey, bytesValue, referenceQueue));
        }
        Thread.currentThread().join();
    }

    static class MybObject extends PhantomReference<byte[]> {
    
    
        private Object key;

        MybObject(Object key, byte[] referent, ReferenceQueue<? super byte[]> q) {
    
    
            super(referent, q);
            this.key = key;
        }
    }
}

resultado de la operación

gc:1
gc:2
...
gc:792
gc:793
gc:794

Se puede ver que no hay reciclaje hasta que se recupere 794 aquí, y los 206 objetos restantes todavía están en la memoria del montón y no se han liberado.

Nota: El resultado no es el mismo en todas las computadoras, está relacionado con la computadora, la configuración y los parámetros de jvm, y los resultados son diferentes cada vez

Clase más limpia

En la figura a continuación, puede ver que Cleaner hereda la clase de referencia virtual PhantomRefrence, que es esencialmente una Refrence.

imagen-20220424103149881

El ReferenceHandler buscará continuamente el objeto de referencia de la lista pendiente. El tipo del objeto de referencia puede ser de cualquier tipo. Cuando el objeto de referencia es un Cleaner, se llama directamente al método clean del Cleaner.

El método de uso también es muy simple, pase la instancia del objeto y el subproceso ejecutable, y cuando el objeto se recicle, se activará el método de ejecución del ejecutable.

Cleaner.create(this, new Runnable() {
    
    
    @Override
    public void run() {
    
    

    }
});

En la mayoría de los casos, se usan referencias seguras en el desarrollo. En algunos escenarios comerciales especiales, también se usan otros tipos de referencia en el modo de desarrollo. Se recomienda que lea este artículo detenidamente y lo use según corresponda.

Supongo que te gusta

Origin blog.csdn.net/qq_21046665/article/details/124094802
Recomendado
Clasificación