Tipos de referência em JAVA, referência forte referência suave referência fraca referência virtual

referência forte

Ambos são referências fortes. Referências fortes são mais comumente usadas no desenvolvimento. Enquanto houver referências a objetos, elas não serão recicladas pelo GC

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

referência suave

Objetos gerais não serão reciclados. Quando a memória do aplicativo estiver prestes a se esgotar -> quando o OOM estiver prestes a ocorrer, o processador de lixo irá reciclá-lo, o que pode impedir que o aplicativo fique inoperante e indisponível devido ao OOM.

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

citação fraca

Quando a referência raiz se tornar inválida, ela será reciclada quando o GC for acionado na próxima vez. Mesmo que a referência raiz seja reatribuída após a reciclagem, a referência não será restabelecida

No processo de varredura da área de memória sob sua jurisdição pelo encadeamento do coletor de lixo, uma vez que um objeto com apenas referências fracas é encontrado, sua memória será recuperada independentemente de o espaço de memória atual ser suficiente ou não.

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

referência fictícia

As referências virtuais não são referências reais e não afetam a coleta de lixo normal.Quando
o coletor de lixo decidir reciclar o objeto PhantomReference, ele o inserirá no 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 um objeto, essa referência é inútil, e é o mesmo que não tê-la. As referências fantasmas devem ser utilizadas em conjunto com a fila de referência, cuja função é acompanhar o processo de coleta de lixo e receber uma notificação do sistema quando o objeto for recuperado pelo coletor. Quando o coletor de lixo estiver prestes a recuperar um objeto, se descobrir que ainda possui uma referência virtual, ele adicionará essa referência virtual à fila de referências após a coleta de lixo e não destruirá completamente o objeto até que sua referência virtual associada seja retirada da fila .

Referências virtuais são frequentemente ReferenceQueueusadas em conjunto com filas de referência

fila de referência

Fila de referência

ReferenceQueue pode ser usado com SoftReference/WeakReference/PhantomReference

refrenceQueueEm essência, é uma lista vinculada, que garante synchronizeda segurança do encadeamento, mantém volatileo objeto modificado head, representa o objeto principal atual e mantém o número atual de filas

Veja como os problemas de segurança de threads são garantidos

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

Pode-se ver que um objeto de bloqueio global é usado aqui. Este é um objeto vazio, que existe apenas como um objeto de bloqueio. Seu núcleo é apenas para bloquear removemétodos e enqueuemétodos (entrar na fila e remover a fila)

synchronized(lock){
    
    
	// ...
}

Quando chamamos removeum método, o método será chamado para Object.waitbloquear, e quando o gc for acionado, enqueueo notifyAll será chamado para notificar a thread, o que não é uma operação cas, portanto tem pouco impacto na performance

O seguinte simula um cenário: quando nosso objeto é reciclado por gc, a chave de referência no mapa é removida.

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 da operação

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

Pode-se ver que não há reciclagem até que 794 sejam recuperados aqui, e os 206 objetos restantes ainda estão na memória heap e não foram liberados

Observação: o resultado não é o mesmo em todos os computadores, está relacionado ao computador, à configuração e aos parâmetros jvm, e os resultados são diferentes a cada vez

Classe de limpeza

Na figura abaixo, você pode ver que o Cleaner herda a classe de referência virtual PhantomRefrence, que é essencialmente uma Refrence.

imagem-20220424103149881

O ReferenceHandler irá buscar continuamente o objeto de referência da lista pendente. O tipo do objeto de referência pode ser de qualquer tipo. Quando o objeto de referência é um Cleaner, o método clean do Cleaner é chamado diretamente.

O método de uso também é muito simples, passe a instância do objeto e a thread executável, e quando o objeto for reciclado, o método run do executável será acionado

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

    }
});

Na maioria dos casos, referências fortes são usadas no desenvolvimento. Em alguns cenários de negócios especiais, outros tipos de referência também são usados ​​no modo de desenvolvimento. É recomendável que você leia este artigo com atenção e use-o conforme apropriado.

Acho que você gosta

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