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ência | get() retorna o objeto? | O GC mantém vivo? | Uso típico |
|---|---|---|---|
| Strong | Sempre | Sempre (enquanto fortemente acessível) | Variáveis e campos comuns |
| Soft | Até a memória ficar baixa | Até o heap estar sob pressão | Caches sensíveis à memória |
| Weak | Até o próximo GC limpá-la | Não | Mapas de canonicalização, listeners |
| Phantom | Nunca (sempre null) | Não | Limpeza 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 collectedReferê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".
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 chamadasSystem.gc()no meio, um objeto fortemente acessível nunca é candidato à coleta — referências strong sempre vencem. weak.get()retornouResource(weakly-held)antes do GC, masnulldepois. Uma vez que o único link forte (onlyWeaklyHeld) foi definido comonull, o objeto ficou apenas weakly reachable, portanto a próxima coleta limpou a referência weak.weak ref enqueued? : trueconfirma que o GC colocou aWeakReferencelimpa em suaReferenceQueue. Esse enfileiramento é o mecanismo de notificação — é como oWeakHashMape os registros de listeners sabem que uma entrada pode ser removida.soft.get()ainda retornouResource(cache-entry)após ogc(). 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()imprimiunullmesmo antes de qualquer coleta, porémphantom enqueued? : truemostra 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:
- Java Garbage Collection — o que "acessível" significa e quando o coletor realmente é executado.
- Java Memory Model — como objetos, threads e visibilidade se encaixam.
- Java Stack vs Heap — onde os objetos para os quais suas referências apontam realmente vivem.
- Java HashMap — o contraparte de chave forte ao
WeakHashMapusado acima.