Existem 4 tipos de referências em Java: StrongReference, SoftReference, WeakReference e PhantomReference (a lendária referência fantasma haha),
Esses quatro tipos de referências estão intimamente relacionados ao GC. Vejamos suas definições e cenários de uso um por um:
1. Referência Forte
StrongReference é a implementação de referência padrão do Java. Ele sobreviverá na JVM pelo maior tempo possível. Quando nenhum objeto apontar para ele, ele será reciclado após a execução do GC.
Código Java
Copie o código do código da seguinte forma:
@Teste
public void referênciaforte() {
Referente do objeto = new Object();
/**
* Crie StrongReference por meio de atribuição
*/
Objeto StrongReference = referente;
assertSame(referente, referência forte);
referente = nulo;
System.gc();
/**
* StrongReference não será reciclado após GC
*/
assertNotNull(strongReference);
}
2. WeakReference e WeakHashMap
WeakReference, como o nome sugere, é uma referência fraca. Quando o objeto referenciado não possui mais uma referência forte na JVM, a referência fraca será automaticamente reciclada após o GC.
Copie o código do código da seguinte forma:
@Teste
public void referência fraca() {
Referente do objeto = new Object();
WeakReference<Object> fracoRerference = new WeakReference<Object>(referente);
assertSame(referente, fracoRerference.get());
referente = nulo;
System.gc();
/**
* Uma vez que não há nenhuma referência forte apontando para o referente, a referência fraca será automaticamente reciclada após o GC
*/
assertNull(weakRerference.get());
}
WeakHashMap usa WeakReference como chave. Quando não houver uma referência forte à chave, WeakHashMap excluirá automaticamente a entrada relevante após o GC.
Copie o código do código da seguinte forma:
@Teste
public void fracoHashMap() lança InterruptedException {
Map<Object, Object> fracoHashMap = new WeakHashMap<Object, Object>();
Chave do objeto = new Object();
Valor do objeto = new Object();
fracoHashMap.put(chave, valor);
assertTrue(weakHashMap.containsValue(valor));
chave = nulo;
System.gc();
/**
* Aguarde até que entradas inválidas entrem no ReferenceQueue para que possam ser limpas na próxima vez que getTable for chamado
*/
Thread.sleep(1000);
/**
* Quando não houver uma referência forte à chave, o WeakHashMap excluirá automaticamente a entrada relevante após o GC
*/
assertFalse(weakHashMap.containsValue(valor));
}
3.SoftReferência
SoftReference tem basicamente as mesmas características do WeakReference. A maior diferença é que o SoftReference reterá a referência pelo maior tempo possível até que a JVM fique sem memória antes de ser reciclada (garantia de máquina virtual). Esse recurso torna o SoftReference muito adequado para aplicativos de cache.
Copie o código do código da seguinte forma:
@Teste
public void softReference() {
Referente do objeto = new Object();
SoftReference<Object> softRerference = new SoftReference<Object>(referente);
assertNotNull(softRerference.get());
referente = nulo;
System.gc();
/**
* referências suaves só serão recicladas antes do jvm OutOfMemory, por isso é muito adequado para aplicativos de cache
*/
assertNotNull(softRerference.get());
}
4. Referência Fantasma
Como protagonista deste artigo, Phantom Reference é muito diferente de WeakReference e SoftReference, pois seu método get() sempre retorna null, daí vem seu nome.
Código Java
Copie o código do código da seguinte forma:
@Teste
public void fantasmaReferenceAlwaysNull() {
Referente do objeto = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<Object>(referente, new ReferenceQueue<Object>());
/**
* O método get da referência fantasma sempre retorna nulo
*/
assertNull(phantomReference.get());
}
Você pode perguntar: qual é a utilidade de uma referência que sempre retorna nulo? Preste atenção ao segundo parâmetro ReferenceQueue ao construir PhantomReference (na verdade, WeakReference e SoftReference também podem ter esse parâmetro).
O único uso de PhantomReference é rastrear quando o referente é enfileirado no ReferenceQueue.
5. Fila de Relevância
Quando um WeakReference começa a retornar nulo, o objeto para o qual ele aponta está pronto para ser reciclado. Nesse momento, algum trabalho de limpeza apropriado pode ser feito. Passe um ReferenceQueue para o construtor de um Reference. irá automaticamente O objeto é inserido no ReferenceQueue WeakHashMap usa o ReferenceQueue para limpar entradas cujas chaves não possuem mais referências fortes.
Código Java
Copie o código do código da seguinte forma:
@Teste
public void referenceQueue() lança InterruptedException {
Referente do objeto = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
WeakReference<Object> fracoReference = new WeakReference<Object>(referente, referenceQueue);
assertFalse(weakReference.isEnqueued());
Referência<? estende Objeto> pesquisado = referenceQueue.poll();
assertNull(pesquisado);
referente = nulo;
System.gc();
assertTrue(weakReference.isEnqueued());
Referência<? estende Objeto> removido = referenceQueue.remove();
assertNotNull(removido);
}
6. PhantomReference vs WeakReference
PhantomReference tem dois benefícios: primeiro, permite-nos saber exatamente quando o objeto é excluído da memória. Este recurso pode ser usado para algumas necessidades especiais (como Distributed GC, XWork e google-guice também usam PhantomReference para fazer alguns trabalhos de limpeza).
Em segundo lugar, pode evitar alguns problemas fundamentais causados pela finalização. Como mencionado acima, a única função do PhantomReference é rastrear quando o referente é enfileirado no ReferenceQueue, mas o WeakReference também possui funções correspondentes.
Trata-se do método finalize do objeto. Este método será chamado antes de gc ser executado. Se um objeto sobrecarregar o método finalize e criar deliberadamente uma referência forte para si mesmo dentro do método, isso fará com que esta rodada de GC não seja possível. reciclado. Este objeto pode causar qualquer GC. O resultado final é que há muito lixo na JVM, mas OutOfMemory Este problema pode ser evitado usando PhantomReference, porque PhantomReference está em finalização. Ele é reciclado após a execução do método, o que significa que é impossível obter a referência original neste momento, portanto o problema acima não ocorrerá. Claro, este é um exemplo muito extremo e geralmente não ocorre.
7. Comparação
Referências suaves vs fracas vs fantasmas | ||||
---|---|---|---|---|
Tipo | Propósito | Usar | Quando GCed | Implementando Classe |
Referência Forte | Uma referência comum Mantém os objetos vivos enquanto eles são referenciados. | referência normal. | Qualquer objeto não apontado pode ser recuperado. | padrão |
Referência suave | Mantém os objetos ativos desde que haja memória suficiente. | para manter os objetos vivos mesmo após os clientes terem removido suas referências (caches sensíveis à memória), caso os clientes comecem a solicitá-los novamente por chave. | Após uma primeira passagem de gc, a JVM decide que ainda precisa recuperar mais espaço. | java.lang.ref.SoftReference |
Referência Fraca | Mantém os objetos ativos apenas enquanto estão em uso (acessíveis) pelos clientes. | Contêineres que excluem automaticamente objetos que não estão mais em uso. | Depois que gc determina que o objeto é apenas fracamente alcançável | java.lang.ref.WeakReference java.util.WeakHashMap |
Referência Fantasma | Permite limpar após a finalização, mas antes que o espaço seja recuperado (substitui ou aumenta o uso definalize()) | Processamento especial de limpeza | Após a finalização. | java.lang.ref.PhantomReference |
8. Resumo
As aplicações gerais não envolverão programação de referência, mas compreender esse conhecimento será útil para compreender o princípio de funcionamento do GC e o ajuste de desempenho. Também pode ser usado na implementação de alguns recursos básicos, como cache.