Qual é a diferença entre SoftReference e WeakReference em Java?

811

Qual é a diferença entre java.lang.ref.WeakReferencee java.lang.ref.SoftReference?

driekken
fonte
9
SoftReferences são tipos de (não realmente, mas para fins de discussão) WeakReferences, que geralmente são coletados quando a JVM pensa que está com falta de memória.
Ajeet Ganga
5
@AjeetGanga, os árbitros fracos fracos são sempre coletados sempre que o GC é executado. Veja stackoverflow.com/a/46291143/632951
Pacerier

Respostas:

927

From Understanding Weak References , de Ethan Nicholas:

Referências fracas

Uma referência fraca , simplificando, é uma referência que não é forte o suficiente para forçar um objeto a permanecer na memória. Referências fracas permitem que você aproveite a capacidade do coletor de lixo de determinar a acessibilidade para você, para que você não precise fazer isso sozinho. Você cria uma referência fraca como esta:

WeakReference weakWidget = new WeakReference(widget);

e depois em outro lugar no código que você pode usar weakWidget.get()para obter o Widgetobjeto real . É claro que a referência fraca não é forte o suficiente para impedir a coleta de lixo; portanto, você pode descobrir (se não houver referências fortes ao widget) que de weakWidget.get()repente começa a retornar null.

...

Referências suaves

Uma referência suave é exatamente como uma referência fraca, exceto que é menos ansiosa para jogar fora o objeto ao qual se refere. Um objeto que é apenas fracamente acessível (as referências mais fortes a ele são WeakReferences) será descartado no próximo ciclo de coleta de lixo, mas um objeto que é levemente acessível geralmente fica por um tempo.

SoftReferencesnão é necessário que ele se comporte de maneira diferente WeakReferences, mas, na prática, objetos que são facilmente alcançáveis ​​geralmente são mantidos enquanto a memória estiver em abundância. Isso os torna uma excelente base para um cache, como o cache de imagem descrito acima, pois você pode deixar o coletor de lixo se preocupar tanto com a acessibilidade dos objetos (um objeto altamente alcançável nunca será removido do cache) quanto com a gravidade do problema. precisa da memória que eles estão consumindo.

E Peter Kessler acrescentou em um comentário:

O Sun JRE trata as SoftReferences de maneira diferente das WeakReferences. Tentamos manter o objeto referenciado por um SoftReference se não houver pressão na memória disponível. Um detalhe: a política para os JRE "-client" e "-server" são diferentes: o JRE -client tenta manter sua pegada pequena, preferindo limpar SoftReferences em vez de expandir o heap, enquanto o JRE -server tenta manter seu alto desempenho, preferindo expandir a pilha (se possível) em vez de limpar SoftReferences. Um tamanho não serve para todos.

Michael Myers
fonte
7
Pós não mais disponível, você pode encontrá-lo na máquina do tempo: web.archive.org/web/20061130103858/http://weblogs.java.net/blog/...
riccardo.tasso
desta vez, o arquivo não está mais disponível
user1506104 27/05
209

Referências fracas são coletadas ansiosamente. Se o GC descobrir que um objeto é fracamente acessível (acessível apenas por referências fracas), ele limpará as referências fracas a esse objeto imediatamente. Como tal, eles são bons para manter uma referência a um objeto para o qual seu programa também mantém (fortemente referenciadas) "informações associadas" em algum lugar, como informações de reflexão em cache sobre uma classe ou um invólucro para um objeto, etc. não faz sentido manter o objeto associado ao GC-ed. Quando a referência fraca é limpa, ela é enfileirada em uma fila de referência que seu código pesquisa em algum lugar, além de descartar os objetos associados. Ou seja, você mantém informações extras sobre um objeto, mas essas informações não são necessárias quando o objeto a que ele se refere desaparece. Na realidade, em determinadas situações, você pode até subclassificar WeakReference e manter as informações extras associadas sobre o objeto nos campos da subclasse WeakReference. Outro uso típico do WeakReference é em conjunto com o Maps para manter instâncias canônicas.

As SoftReferences, por outro lado, são boas para armazenar em cache recursos externos recreativos, pois o GC normalmente atrasa a limpeza. É garantido, no entanto, que todas as SoftReferences serão limpas antes da saída do OutOfMemoryError, portanto, teoricamente, elas não podem causar um OOME [*].

O exemplo típico de caso de uso é manter uma forma analisada de um conteúdo de um arquivo. Você implementaria um sistema no qual carregaria um arquivo, analisaria e manteria um SoftReference no objeto raiz da representação analisada. Da próxima vez que você precisar do arquivo, tente recuperá-lo através do SoftReference. Se você pode recuperá-lo, poupou-se de outra carga / análise e, se o GC o limpou nesse meio tempo, você o recarregou. Dessa forma, você utiliza memória livre para otimização de desempenho, mas não arrisca um OOME.

Agora para o [*]. Manter um SoftReference não pode causar um OOME em si. Se, por outro lado, você usa o SoftReference por engano para uma tarefa que um WeakReference deve ser usado (a saber, você mantém as informações associadas a um Objeto de alguma forma fortemente referenciadas e as descarta quando o objeto de Referência é limpo), você pode executar o OOME como seu código que pesquisa o ReferenceQueue e descarta os objetos associados pode não ser executado em tempo hábil.

Portanto, a decisão depende do uso - se você estiver armazenando em cache informações que são caras de construir, mas mesmo assim reconstrutíveis a partir de outros dados, use referências flexíveis - se você estiver mantendo uma referência a uma instância canônica de alguns dados ou desejar tenha uma referência a um objeto sem "possuí-lo" (impedindo-o de ser GC'd), use uma referência fraca.

driekken
fonte
14
Particularmente útil para explicar quando objetos fracos seriam usados.
precisa saber é o seguinte
Um ponto-chave sobre o uso adequado de a WeakReferenceé que, em locais onde a pessoa deve usá-la, o fato de permanecer válido por um tempo após a referência sair do escopo pode ser tolerável, mas não é desejável.
Supercat 24/02
Estou lutando para entender o que é o uso do WeakHashMap se ele sempre produz uma referência fraca ao seu objeto de valor-chave?
@supercat, Existe apenas um uso adequado WeakReferencepara observar as execuções do GC. Veja elaboração: stackoverflow.com/a/46291143/632951
Pacerier
1
@ Pacerier: O autor desse post está simplesmente errado. Ele negligencia alguns outros cenários de uso, como a inscrição de eventos, seu segundo ponto é absurdo e seu terceiro ponto assume que um programador pode fazer coisas que talvez não sejam possíveis. Seu primeiro argumento é razoável, mas se relaciona diretamente com o que eu disse. Se o código freqüentemente precisar criar e comparar objetos imutáveis ​​grandes, por exemplo, a parte da construção será mais barata se o código criar novos objetos sem considerar se eles já existem, mas será feita uma comparação entre um objeto e ele próprio (referências idênticas). ...
supercat
155

Em Java ; ordem do mais forte para o mais fraco, existem: Forte, Suave, Fraco e Fantasma

Uma referência Forte é uma referência normal que protege o objeto referido da coleção pelo GC. ou seja, nunca coleta lixo.

Uma referência suave é qualificada para coleta pelo coletor de lixo, mas provavelmente não será coletada até que sua memória seja necessária. ou seja, o lixo é coletado antes OutOfMemoryError.

Uma referência fraca é uma referência que não protege um objeto referenciado da coleção pelo GC. ou seja, o lixo é coletado quando não há referências de Forte ou Macio.

Uma referência fantasma é uma referência a um objeto que é referenciada fantasmamente após sua finalização, mas antes de sua memória alocada ter sido recuperada.

Fonte

Analogia: Suponha que uma JVM é um reino, Object é um rei do reino e GC é um atacante do reino que tenta matar o rei (objeto).

  • Quando King é Strong , GC não pode matá-lo.
  • Quando King é mole , GC o ataca, mas King governa o reino com proteção até que os recursos estejam disponíveis.
  • Quando King é fraco , GC o ataca, mas governa o reino sem proteção.
  • Quando o rei é Phantom , GC já o matou, mas o rei está disponível através de sua alma.
Premraj
fonte
7
Referência suave ... until memory is availablenão faz sentido. Você quer dizer is eligible for collection by garbage collector, but probably won't be collected until its memory is needed for another use?
Home
1
Sim, o coletor de lixo não coletará a referência até que a memória esteja disponível.
Premraj
2
Eu gosto de coisas simples e explicadas, sem muito bla bla bla +1 de mim!
Adelin
3
Excelente resumo com exemplo inovador
Ravindra babu
+1, leitura adicional: javarevisited.blogspot.in/2014/03/…
roottraveller
77

Referência fraca http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ref/WeakReference.html

Princípio: weak reference está relacionado à coleta de lixo. Normalmente, objeto com um ou maisreference não será elegível para coleta de lixo.
O princípio acima não é aplicável quando é weak reference. Se um objeto tiver apenas uma referência fraca com outros objetos, estará pronto para a coleta de lixo.

Vejamos o exemplo abaixo: Temos um Map com objetos onde Key é referência a um objeto.

import java.util.HashMap;   
public class Test {

    public static void main(String args[]) {
        HashMap<Employee, EmployeeVal> aMap = new 
                       HashMap<Employee, EmployeeVal>();

        Employee emp = new Employee("Vinoth");
        EmployeeVal val = new EmployeeVal("Programmer");

        aMap.put(emp, val);

        emp = null;

        System.gc();
        System.out.println("Size of Map" + aMap.size());

    }
}

Agora, durante a execução do programa que fizemos emp = null . A Maprealização a chave não faz sentido aqui como é null. Na situação acima, o objeto não é coletado como lixo.

WeakHashMap

WeakHashMap é aquele em que as entradas (key-to-value mappings ) serão removidas quando não for mais possível recuperá-las do Map.

Deixe-me mostrar o exemplo acima mesmo com o WeakHashMap

import java.util.WeakHashMap;

public class Test {

    public static void main(String args[]) {
        WeakHashMap<Employee, EmployeeVal> aMap = 
                    new WeakHashMap<Employee, EmployeeVal>();

        Employee emp = new Employee("Vinoth");
        EmployeeVal val = new EmployeeVal("Programmer");

        aMap.put(emp, val);

        emp = null;

        System.gc();
        int count = 0;
        while (0 != aMap.size()) {
            ++count;
            System.gc();
        }
        System.out.println("Took " + count
                + " calls to System.gc() to result in weakHashMap size of : "
                + aMap.size());
    }
}

Saída: levou 20 calls to System.gc()para resultar emaMap size : 0.

WeakHashMaptem apenas referências fracas às chaves, não referências fortes como outras Mapclasses. Há situações em que você deve tomar cuidado quando o valor ou a chave é fortemente referenciado, embora você tenha usado WeakHashMap. Isso pode ser evitado envolvendo o objeto em um WeakReference .

import java.lang.ref.WeakReference;
import java.util.HashMap;

public class Test {

    public static void main(String args[]) {
        HashMap<Employee, EmployeeVal> map = 
                      new HashMap<Employee, EmployeeVal>();
        WeakReference<HashMap<Employee, EmployeeVal>> aMap = 
                       new WeakReference<HashMap<Employee, EmployeeVal>>(
                map);

        map = null;

        while (null != aMap.get()) {
            aMap.get().put(new Employee("Vinoth"),
                    new EmployeeVal("Programmer"));
            System.out.println("Size of aMap " + aMap.get().size());
            System.gc();
        }
        System.out.println("Its garbage collected");
    }
}

Referências suaves.

Soft Referenceé um pouco mais forte que a referência fraca. A referência suave permite a coleta de lixo, mas pede ao coletor de lixo que a limpe apenas se não houver outra opção.

O coletor de lixo não coleta agressivamente objetos de fácil acesso, como ocorre com objetos de fácil acesso - em vez disso, apenas coleta objetos de fácil acesso se realmente "precisar" da memória. Referências suaves são uma maneira de dizer ao coletor de lixo: "Enquanto a memória não estiver muito tensa, eu gostaria de manter esse objeto por perto. Mas se a memória ficar muito tensa, vá em frente e colete-a, e eu resolvo com isso." O coletor de lixo é necessário para limpar todas as referências suaves antes que ele possa ser lançado OutOfMemoryError.

Thalaivar
fonte
5
Você pode obter uma NullPointerExceptionno aMap.get().put(...).
xehpuk
Seu primeiro exemplo de HashMap parece errado. Quando você faz "aMap.put (emp, val);" 'emp' e 'val' são fortes referências. Internamente, uma nova variável é criada para conter 'emp' e 'val'; portanto, quando você faz "emp = null;" você está apenas anulando a variável "emp", mas não a variável internamente no mapa de hash (que ainda mantém o objeto Employee original). Portanto, o mapa de hash ainda manterá uma forte referência ao 'emp', independentemente do que você faz com a variável 'emp' fora.
Tiago
@Tiago. Não. Presumivelmente, por "primeiro exemplo", você está se referindo ao WeakHashMapexemplo (já que esse é o primeiro que está demonstrando comportamento fraco). Veja o documento para "WeakHashMap": "An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. " O ponto principal de usar o WeakHashMap é que você não precisa declarar / passar um WeakReference; o WeakHashMap faz isso por você, internamente. docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html
ToolmakerSteve
Recebeu 0 chamadas para System.gc () para resultar em um tamanho fraco de HashMap: 0 é a saída do seu segundo programa?
Adelin
1
Para outro exemplo de WeakHashMapem ação, com o exemplo de aplicativo mostrando como as entradas são removidas somente após a execução da coleta de lixo, consulte minha Resposta à pergunta: O WeakHashMap está sempre crescendo ou limpa as chaves do lixo? .
Basil Bourque
50

A única diferença real entre uma referência suave e uma referência fraca é que

o coletor de lixo usa algoritmos para decidir se deve ou não recuperar um objeto de fácil acesso, mas sempre recupera um objeto de difícil acesso.

Samir Mangroliya
fonte
@ATorras, Samir. Eu expandi sobre esta resposta aqui: stackoverflow.com/a/46291143/632951
Pacerier 19/17
25

SoftReferencefoi projetado para caches. Quando for descoberto que um faz WeakReferencereferência a um objeto inacessível, ele será limpo imediatamente. SoftReferencepode ser deixado como está. Normalmente, existe algum algoritmo relacionado à quantidade de memória livre e ao tempo usado pela última vez para determinar se ela deve ser limpa. O algoritmo atual da Sun é limpar a referência se ela não for usada em tantos segundos quanto houver megabytes de memória livre no heap Java (configurável, o HotSpot do servidor verifica o heap máximo possível, conforme definido por -Xmx). SoftReferences serão limpos antes de OutOfMemoryErrorserem lançados, a menos que seja alcançável de outra forma.

Tom Hawtin - linha de orientação
fonte
9
Mas no Android não é recomendado para caches developer.android.com/reference/java/lang/ref/…
Yaroslav Mytkalyk
4
@DoctororDrive tbf a pergunta era sobre java, não dalvik! :-P
fabspro
2
@YaroslavMytkalyk, sinceramente, se o Android quiser reescrever o comportamento de uma classe, ele deve usar seu próprio espaço para nome, não java.lang. Tal abuso de sinônimos não está fazendo bem a ninguém.
Pacerier 19/09/17
9

Este artigo pode ser super útil para entender referências fortes, suaves, fracas e fantasmas.


Para lhe dar um resumo,

Se você tiver apenas referências fracas a um objeto (sem referências fortes), o objeto será recuperado pelo GC no próximo ciclo do GC.

Se você tiver apenas referências simples para um objeto (sem referências fortes), o objeto será recuperado pelo GC somente quando a JVM ficar sem memória.


Então, você pode dizer que referências fortes têm o poder máximo (nunca podem ser coletadas pelo GC)

Referências suaves são poderosas que referências fracas (pois podem escapar do ciclo do GC até a JVM ficar sem memória)

Referências fracas são ainda menos poderosas que as referências flexíveis (como não podem exceder nenhum ciclo do GC e serão recuperadas se o objeto não tiver outra referência forte).


Analogia do restaurante

  • Garçom - GC
  • Você - Objeto na pilha
  • Área / espaço para restaurantes - Espaço de pilha
  • Novo cliente - novo objeto que deseja tabela no restaurante

Agora, se você é um cliente forte (referência análoga a forte), mesmo que um novo cliente chegue ao restaurante ou o que quer que seja, nunca sairá da sua mesa (a área de memória na pilha). O garçom não tem o direito de pedir para você (ou até mesmo solicitar) para sair do restaurante.

Se você é um cliente flexível (semelhante à referência suave), se um novo cliente entrar no restaurante, o garçom não solicitará que você saia da mesa, a menos que não haja outra mesa vazia para acomodar o novo cliente. (Em outras palavras, o garçom solicitará que você saia da mesa apenas se um novo cliente entrar e não houver outra mesa para esse novo cliente)

Se você é um cliente fraco (referência análoga a fraca), o garçom, por sua vontade, pode (a qualquer momento) pedir para você sair do restaurante: P

Lavish Kothari
fonte
7

A Única Diferença Real

De acordo com o documento , as WeakReferences frouxas devem ser limpas por um GC em execução.

De acordo com o documento , as SoftReferences soltas devem ser limpas antes que o OOM seja lançado.

Essa é a única diferença real. Tudo o resto não faz parte do contrato. (Presumo que os documentos mais recentes sejam contratuais.)

SoftReferences são úteis. Os caches sensíveis à memória usam SoftReferences, não WeakReferences.


O único uso adequado do WeakReference é observar a execução do GC. Você faz isso criando um novo WeakReference cujo objeto fica imediatamente fora do escopo e tenta sair nulo weak_ref.get(). Quando é null, você aprende que, entre essa duração, o GC foi executado.

Quanto ao uso incorreto do WeakReference, a lista é interminável:

  • um truque ruim para implementar a softreferência de prioridade 2, de modo que você não precise escrever uma, mas não funciona conforme o esperado, porque o cache seria limpo a cada execução do GC, mesmo quando houver memória disponível. Consulte https://stackoverflow.com/a/3243242/632951 para obter phails. (Além disso, e se você precisar de mais de 2 níveis de prioridade de cache? Você ainda precisará de uma biblioteca real para isso.)

  • um péssimo hack para associar dados a um objeto de uma classe existente, mas cria um vazamento de memória (OutOfMemoryError) quando o seu GC decide fazer uma pausa após a criação de suas fracas referências. Além disso, é muito feio: uma abordagem melhor é usar tuplas.

  • um truque ruim para associar dados a um objeto de uma classe existente, em que a classe tem a coragem de se tornar não subclassável e é usada em um código de função existente que você precisa chamar. Nesse caso, a solução adequada é editar a classe e torná-la subclassável, ou editar a função e fazer com que ela use uma interface em vez de uma classe ou use uma função alternativa.

Pacerier
fonte
E um cache em que o tipo de chave equals()é apenas identidade do objeto? Referências suaves parecem um desperdício lá, porque uma vez que um objeto-chave não é mais alcançável, ninguém mais procurará esse mapeamento.
precisa saber é o seguinte
Discordo. Use WeakReference quando não quiser influenciar o GC de qualquer maneira (você pode salvar uma referência de objeto e depois verificar mais tarde se ela ainda existe, sem nenhuma preferência). Use o SoftReference se quiser influenciar o GC a tentar manter o objeto (por exemplo, quando você prefere que o GC o mantenha).
David Refaeli #
Um bom exemplo de onde usar o WeakReference está no AsyncTask do Android - para manter uma instância do contexto. Dessa forma, se o contexto morrer (se houver atividade - rotação da tela etc.), o AsyncTask não terá nenhuma referência forte e poderá ser coletado como lixo. Verifique youtu.be/…
David Refaeli
3

Os seis tipos de estados de acessibilidade de objetos em Java:

  1. Fortes objetos ly alcançáveis - GC não vai recolher ( recuperar a memória ocupada por ) esse tipo de objeto. Eles são acessíveis através de um nó raiz ou outro objeto fortemente acessível (ou seja, através de variáveis ​​locais, variáveis ​​de classe, variáveis ​​de instância, etc.)
  2. Macio ly objetos acessíveis - GC pode tentar a recolher este tipo de objeto, dependendo contenção de memória. Eles são acessíveis a partir da raiz por meio de um ou mais objetos de referência programável
  3. Fracas objetos ly alcançáveis - GC deve recolher esse tipo de objeto. Estes são alcançáveis ​​a partir da raiz através de um ou mais objetos de referência fracos
  4. Objetos ressuscitáveis - o GC já está no processo de coletar esses objetos. Mas eles podem voltar para um dos estados - Forte / Suave / Fraco pela execução de algum finalizador
  5. Objeto fantasma acessível - O GC já está no processo de coletar esses objetos e determinou que não pode ser ressuscitado por nenhum finalizador (se ele próprio declara um método finalize (), seu finalizador será executado) . Estes são alcançáveis ​​a partir da raiz através de um ou mais objetos de referência fantasma
  6. Objeto inacessível - Um objeto não é acessível de maneira forte, suave, fraca ou fantasma e não pode ser ressuscitado. Esses objetos estão prontos para recuperação

Para mais detalhes: https://www.artima.com/insidejvm/ed2/gc16.html «recolher

V.Vidyasagar
fonte
4
Não é uma boa descrição de referências fantasmas. Além disso, você listou os quatro tipos em uma ordem peculiar. "fantasma" é o tipo mais fraco, não o tipo mais forte. A ordem tradicional de listá-las é "forte, suave, fraca, fantasma". E eu não tenho idéia de onde você teve a noção de que objetos fantasmas são usados ​​para mecanismos de cache. AFAIK, eles são um estado temporário visto apenas pelo GC, não algo com o qual um programador comum trabalharia.
Home
@ToolmakerSteve e tudo - Desculpas por algumas coisas 1. a explicação errada das referências do Phantom na versão anterior da minha resposta e 2. Atraso na correção dos erros. Agora, a resposta foi melhorado, corrigindo os erros
V.Vidyasagar
1

Deve-se estar ciente de que um objeto com pouca referência só será coletado quando tiver APENAS referências fracas. Se tiver apenas uma referência forte, ele não será coletado, não importa quantas referências fracas tenha.

Fai Lau
fonte
Isso é senso comum ... o mesmo vale para softref e phantomref.
Pacerier 19/09/17
1

Para dar um aspecto de uso da memória em ação, fiz um experimento com referências Forte, Suave, Fraca e Fantasma sob carga pesada com objetos pesados, mantendo-as até o final do programa. Em seguida, monitorou o uso de heap e o comportamento do GC . Essas métricas podem variar caso a caso, mas certamente oferecem um alto nível de entendimento. Abaixo estão os resultados.

Heap e comportamento do GC sob carga pesada

  • Referência Forte / Difícil - Como o programa continuou, a JVM não pôde coletar o objeto referenciado forte retido. Eventualmente, acabou em "java.lang.OutOfMemoryError: espaço de heap Java"
  • Referência suave - À medida que o programa continuava, o uso de heap continuava crescendo, mas o OLD gen GC aconteceu quando estava se aproximando do heap máximo. O GC começou pouco depois, após o início do programa.
  • Referência fraca - Quando o programa foi iniciado, os objetos começaram a finalizar e a serem coletados quase imediatamente. Principalmente os objetos foram coletados na coleta de lixo da geração jovem.
  • Referência fantasma - Semelhante à referência fraca, os objetos referenciados fantasma também começaram a ser finalizados e o lixo coletado imediatamente. Não havia GC da geração antiga e todos os objetos estavam sendo coletados na própria coleta de lixo da geração jovem.

Você pode obter gráficos mais detalhados , estatísticas, observações para este experimento aqui .

Ravi K
fonte
0

WeakReference : objetos que são pouco referenciados são coletados a cada ciclo do GC (menor ou completo).

SoftReference : quando objetos que são apenas referenciados suavemente são coletados depende de:

  1. -XX: SoftRefLRUPolicyMSPerMB = N sinalizador (o valor padrão é 1000, ou seja, 1 segundo)

  2. Quantidade de memória livre na pilha.

    Exemplo:

    • o heap possui 10 MB de espaço livre (após o GC completo);
    • -XX: SoftRefLRUPolicyMSPerMB = 1000

    O objeto referenciado apenas pelo SoftReference será coletado se a última vez em que foi acessada for maior que 10 segundos.

Artem Petrov
fonte