W3docs

Tipos de Referência Java: Strong, Weak, Soft, Phantom

Como os tipos de referência Java interagem com o coletor de lixo para implementar caches, listeners e limpeza de recursos.

A coleta de lixo parece automática, mas o Java oferece um controle para influenciá-la. Uma variável comum é uma referência forte que mantém um objeto na memória. O pacote java.lang.ref adiciona três categorias mais fracas — soft, weak e phantom — que permitem ao coletor de lixo recuperar um objeto mesmo enquanto você ainda mantém uma referência a ele. Esses tipos de referência são a base de caches sensíveis à memória, registros de listeners sem vazamentos e limpeza confiável de recursos.

A acessibilidade decide quem sobrevive

O coletor de lixo mantém um objeto vivo enquanto ele for acessível a partir de uma raiz do GC (uma thread ativa, um campo estático, uma variável de pilha). A força das referências no caminho até o objeto determina sua classe de acessibilidade, e essa classe decide seu destino quando a memória é necessária.

Referênciaget() retorna o objeto?O GC mantém vivo?Uso típico
StrongSempreSempre (enquanto fortemente acessível)Variáveis e campos comuns
SoftAté a memória ficar baixaAté o heap estar sob pressãoCaches sensíveis à memória
WeakAté o próximo GC limpá-laNãoMapas de canonicalização, listeners
PhantomNunca (sempre null)NãoLimpeza pós-coleta

A ordem do mais forte ao mais fraco é: strong → soft → weak → phantom. Quando várias referências de diferentes intensidades apontam para um objeto, a mais forte vence — uma única referência forte é suficiente para manter um objeto vivo para sempre.

Referências strong: o padrão

Toda referência que você escreve sem a API java.lang.ref é forte. Enquanto uma referência forte for acessível, o objeto não pode ser coletado — esta é a origem da maioria dos vazamentos de memória, onde uma entrada esquecida em uma coleção de longa duração mantém objetos vivos indefinidamente.

List<byte[]> cache = new ArrayList<>();
cache.add(new byte[10_000_000]); // 10 MB pinned by a strong reference
// Nothing here can be collected until 'cache' itself becomes unreachable.

Referências soft: caches sensíveis à memória

Uma SoftReference permite que o coletor recupere o objeto somente quando o heap está com pouco espaço. Enquanto a memória é abundante, get() continua retornando o objeto, o que torna as referências soft ideais para caches que devem diminuir sob pressão em vez de causar um OutOfMemoryError.

SoftReference<BufferedImage> ref = new SoftReference<>(loadThumbnail(path));

BufferedImage cached = ref.get();
if (cached == null) {             // GC reclaimed it under memory pressure
  cached = loadThumbnail(path);   // recompute and re-wrap
  ref = new SoftReference<>(cached);
}
return cached;

A JVM garante que todas as referências soft para objetos softly-reachable são limpas antes de lançar OutOfMemoryError, portanto um cache de referência soft é a última coisa a ser sacrificada, não a primeira.

Referências weak: mapas e listeners

Uma WeakReference não atrasa a coleta de forma alguma. No instante em que um objeto é apenas weakly reachable, ele se torna elegível para o GC e get() retornará null após o próximo ciclo de coleta. Isso é exatamente o que você quer para chaves em um cache que deve desaparecer quando ninguém mais as usar — que é o que o WeakHashMap utiliza internamente.

WeakHashMap<Widget, Metadata> sidecar = new WeakHashMap<>();
sidecar.put(widget, metadata);
// When 'widget' is no longer strongly referenced elsewhere, the entry
// vanishes automatically — no manual remove(), no leak.

Associar uma referência a uma ReferenceQueue permite ser notificado quando o referente é coletado: o GC enfileira a referência limpa para que você possa executar a lógica de acompanhamento.

ReferenceQueue<Widget> queue = new ReferenceQueue<>();
WeakReference<Widget> ref = new WeakReference<>(widget, queue);
// later, on a cleanup thread:
Reference<?> dead = queue.remove(); // blocks until a referent is collected

Referências phantom: limpeza pós-coleta

Uma PhantomReference é a categoria mais fraca e a mais especializada. Seu get() sempre retorna null, portanto você nunca pode ressuscitar o objeto através dela. Seu único propósito é ser enfileirada após o objeto ter sido coletado, fornecendo um gancho seguro para liberar recursos nativos — o substituto moderno e confiável para o finalize() obsoleto.

ReferenceQueue<Object> queue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<>(resource, queue);
// A background thread drains the queue and frees the off-heap buffer
// only once the JVM confirms the object is truly gone.

O próprio java.lang.ref.Cleaner do JDK (Java 9+) é construído sobre referências phantom e é o que você deve usar em código real em vez de gerenciar a fila manualmente.

Um exemplo prático: todos os quatro tipos em uma execução

Este programa cria um objeto mantido apenas por cada tipo de referência, força uma coleta de lixo com System.gc(), e relata o que sobreviveu. Ele também conecta ReferenceQueues às referências weak e phantom para que você possa observar o GC notificando cada "morte".

java— editable, runs on the server

O que observar na execução:

  • A referência strong imprimiu o mesmo Resource(kept-alive) no início e no final. Apesar de duas chamadas System.gc() no meio, um objeto fortemente acessível nunca é candidato à coleta — referências strong sempre vencem.
  • weak.get() retornou Resource(weakly-held) antes do GC, mas null depois. Uma vez que o único link forte (onlyWeaklyHeld) foi definido como null, o objeto ficou apenas weakly reachable, portanto a próxima coleta limpou a referência weak.
  • weak ref enqueued? : true confirma que o GC colocou a WeakReference limpa em sua ReferenceQueue. Esse enfileiramento é o mecanismo de notificação — é como o WeakHashMap e os registros de listeners sabem que uma entrada pode ser removida.
  • soft.get() ainda retornou Resource(cache-entry) após o gc(). O heap não estava sob pressão, portanto o coletor manteve o objeto softly-reachable vivo — exatamente o comportamento que torna as referências soft adequadas para caches que só diminuem quando a memória está apertada.
  • phantom.get() imprimiu null mesmo antes de qualquer coleta, porém phantom enqueued? : true mostra que ainda foi enfileirado assim que seu referente morreu. Uma referência phantom nunca entrega o objeto de volta; ela existe puramente para sinalizar após o fato que a limpeza pode ser executada com segurança.

Tópicos relacionados

Os tipos de referência só fazem sentido em conjunto com como a JVM recupera memória:

Prática

Prática
Você precisa de um cache que armazene valores computados para acelerar seu programa, mas que libere automaticamente quando o heap estiver com pouco espaço em vez de causar um OutOfMemoryError. Qual tipo de referência é mais adequado?
Você precisa de um cache que armazene valores computados para acelerar seu programa, mas que libere automaticamente quando o heap estiver com pouco espaço em vez de causar um OutOfMemoryError. Qual tipo de referência é mais adequado?
Was this page helpful?