Você já usou PhantomReference em algum projeto?


89

A única coisa que sei PhantomReferenceé,

  • Se você usar seu get()método, ele sempre retornará nulle não o objeto. Qual é a utilidade disso?
  • Ao usar PhantomReference, você garante que o objeto não pode ser ressuscitado do finalizemétodo.

Mas qual é a utilidade desse conceito / classe?

Você já usou isso em algum de seu projeto ou tem algum exemplo onde devemos usar isso?



Como você não pode obter o objeto referido de um PhantomReference, é um nome totalmente impróprio: deveria ter sido chamado FakeReferenceou NonReference.
Pacerier

Aqui está outra discussão com o código: stackoverflow.com/q/43311825/632951
Pacerier

Respostas:


47

Usei PhantomReferences em um tipo de perfil de memória simplista e muito especializado para monitorar a criação e destruição de objetos. Eu precisava deles para controlar a destruição. Mas a abordagem está desatualizada. (Foi escrito em 2004 com o objetivo de J2SE 1.4.) As ferramentas de criação de perfis profissionais são muito mais poderosas e confiáveis ​​e os recursos Java 5 mais recentes, como JMX ou agentes e JVMTI, também podem ser usados ​​para isso.

PhantomReferences (sempre usados ​​junto com a fila de referência) são superiores aos finalizeque apresentam alguns problemas e, portanto, devem ser evitados. Principalmente tornando os objetos acessíveis novamente. Isso poderia ser evitado com o idioma do guardião do finalizador (-> leia mais em 'Java Efetivo'). Portanto, eles também são o novo finalize .

Além disso, PhantomReferences

permitem que você determine exatamente quando um objeto foi removido da memória. Na verdade, eles são a única maneira de determinar isso. Isso geralmente não é útil, mas pode ser útil em certas circunstâncias muito específicas, como a manipulação de imagens grandes: se você tem certeza de que uma imagem deve ser coletada como lixo, pode esperar até que realmente seja antes de tentar carregar a próxima imagem e, portanto, torna o temido OutOfMemoryError menos provável. (Citado de enicholas .)

E como o psd escreveu primeiro, Roedy Green tem um bom resumo de referências .


21

Uma explicação geral sobre a tabela dividida , do Glossário Java.

Que, claro, coincide com a documentação PhantomReference :

Objetos de referência fantasma, que são enfileirados após o coletor determinar que seus referentes podem ser recuperados. As referências fantasmas são mais frequentemente usadas para agendar ações de limpeza pré-morte de uma forma mais flexível do que é possível com o mecanismo de finalização Java.

E por último, mas não menos importante, todos os detalhes sangrentos ( esta é uma boa leitura ): Objetos de referência Java (ou como eu aprendi a parar de se preocupar e amar OutOfMemoryError) .

Boa codificação. (Mas para responder à pergunta, eu só usei WeakReferences.)


A propósito, uma pergunta sobre esse artigo. Na seção sobre PhantomReference, ele mantém uma referência forte aos objetos de conexão por meio dessas duas tabelas. Isso significa que as conexões nunca ficarão inacessíveis (supondo que a própria instância do pool nunca se torne inacessível). Portanto, as PhantomReferences correspondentes nunca serão enfileiradas, certo? Ou eu estou esquecendo de alguma coisa?
shrini1000

1
Uau, esse artigo da kdgregory merece um +10
Pacerier

14

Ótima explicação sobre o uso de Phantom Reference:

As referências fantasmas são uma forma segura de saber se um objeto foi removido da memória. Por exemplo, considere um aplicativo que lida com imagens grandes. Suponha que desejemos carregar uma imagem grande na memória quando a imagem grande já estiver na memória e pronta para a coleta de lixo. Nesse caso, queremos esperar até que a imagem antiga seja coletada antes de carregar uma nova. Aqui, a referência fantasma é uma opção flexível e segura para escolher. A referência da imagem antiga será enfileirada no ReferenceQueue assim que o objeto da imagem antiga for finalizado. Depois de receber essa referência, podemos carregar a nova imagem na memória.


12

Eu encontrei um caso prático e útil uso do PhantomReferenceque é org.apache.commons.io.FileCleaningTrackerno projeto commons-io. FileCleaningTrackerirá excluir o arquivo físico quando seu objeto marcador for coletado como lixo.
Algo a se notar é a Trackerclasse que estende a PhantomReferenceclasse.


5

ISTO DEVE SER OBSOLETO COM JAVA 9!
Use em seu java.util.Cleanerlugar! (Ou sun.misc.Cleanerem JRE mais antigo)

Postagem original:


Descobri que o uso de PhantomReferences tem quase a mesma quantidade de armadilhas que os métodos finalizadores (mas menos problemas depois que você acertar). Eu escrevi uma pequena solução (uma estrutura muito pequena para usar PhantomReferences) para Java 8. Ela permite usar expressões lambda como retornos de chamada a serem executados após a remoção do objeto. Você pode registrar os retornos de chamada para recursos internos que devem ser fechados. Com isso eu encontrei uma solução que funciona para mim, pois a torna muito mais prática.

https://github.com/claudemartin/java-cleanup

Aqui está um pequeno exemplo para mostrar como um retorno de chamada é registrado:

  class Foo implements Cleanup {
    //...  
    public Foo() { 
    //...    
      this.registerCleanup((value) -> {
        try {
          // 'value' is 'this.resource'
          value.close();
        } catch (Exception e) {
          logger.warning("closing resource failed", e);
        }
      }, this.resource);
    }

E há o método ainda mais simples de fechamento automático, que faz quase o mesmo que o anterior:

this.registerAutoClose(this.resource);

Para responder às suas perguntas:

[então qual é a utilidade disso]

Você não pode limpar algo que não existe. Mas poderia ter recursos que ainda existem e precisam ser limpos para que possam ser removidos.

Mas qual é a utilidade desse conceito / classe?

Não é necessariamente para fazer nada com nenhum efeito diferente de depuração / registro. Ou talvez para estatísticas. Eu vejo isso mais como um serviço de notificação do GC. Você também pode querer usá-lo para remover dados agregados que se tornam irrelevantes depois que o objeto é removido (mas provavelmente existem soluções melhores para isso). Os exemplos geralmente mencionam conexões de banco de dados a serem fechadas, mas não vejo como isso é uma boa ideia, já que você não poderia trabalhar com transações. Uma estrutura de aplicativo fornecerá uma solução muito melhor para isso.

Você já usou isso em algum projeto seu, ou tem algum exemplo onde devemos usar isso? Ou este conceito é feito apenas para o ponto de vista da entrevista;)

Eu o uso principalmente apenas para registro. Assim, posso rastrear os elementos removidos e ver como o GC funciona e pode ser ajustado. Eu não executaria nenhum código crítico dessa maneira. Se algo precisa ser fechado, isso deve ser feito em uma instrução try-with-resource. E eu o uso em testes de unidade, para ter certeza de que não há nenhum vazamento de memória. Da mesma forma que jontejj o faz. Mas minha solução é um pouco mais geral.


Sim, assim como minha solução "java-cleanup". Ambos são abstrações, então você não precisa lidar com eles diretamente.
Claude Martin

3

Usei um PhantomReference em um teste de unidade para verificar se o código em teste não mantinha referências desnecessárias a algum objeto. ( Código original )

import static com.google.common.base.Preconditions.checkNotNull;
import static org.fest.assertions.Assertions.assertThat;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;

import com.google.common.testing.GcFinalization;

/**
* Helps to test for memory leaks
*/
public final class MemoryTester
{
private MemoryTester()
{
}

/**
* A simple {@link PhantomReference} that can be used to assert that all references to it is
* gone.
*/
public static final class FinalizationAwareObject extends PhantomReference<Object>
{
private final WeakReference<Object> weakReference;

private FinalizationAwareObject(Object referent, ReferenceQueue<Object> referenceQueue)
{
super(checkNotNull(referent), referenceQueue);
weakReference = new WeakReference<Object>(referent, referenceQueue);
}

/**
* Runs a full {@link System#gc() GC} and asserts that the reference has been released
* afterwards
*/
public void assertThatNoMoreReferencesToReferentIsKept()
{
String leakedObjectDescription = String.valueOf(weakReference.get());
GcFinalization.awaitFullGc();
assertThat(isEnqueued()).as("Object: " + leakedObjectDescription + " was leaked").isTrue();
}
}

/**
* Creates a {@link FinalizationAwareObject} that will know if {@code referenceToKeepTrackOff}
* has been garbage collected. Call
* {@link FinalizationAwareObject#assertThatNoMoreReferencesToReferentIsKept()} when you expect
* all references to {@code referenceToKeepTrackOff} be gone.
*/
public static FinalizationAwareObject createFinalizationAwareObject(Object referenceToKeepTrackOff)
{
return new FinalizationAwareObject(referenceToKeepTrackOff, new ReferenceQueue<Object>());
}
}

E o teste :

@Test
public void testThatHoldingOnToAnObjectIsTreatedAsALeak() throws Exception
{
    Object holdMeTight = new String("Hold-me-tight");
    FinalizationAwareObject finalizationAwareObject = MemoryTester.createFinalizationAwareObject(holdMeTight);
    try
    {
    finalizationAwareObject.assertThatNoMoreReferencesToReferentIsKept();
    fail("holdMeTight was held but memory leak tester did not discover it");
    }
    catch(AssertionError expected)
    {
    assertThat(expected).hasMessage("[Object: Hold-me-tight was leaked] expected:<[tru]e> but was:<[fals]e>");
    }
}

2

É comum usar WeakReferenceonde PhantomReferenceé mais apropriado. Isso evita certos problemas de ser capaz de ressuscitar objetos depois que um WeakReferenceé limpo / enfileirado pelo coletor de lixo. Normalmente, a diferença não importa porque as pessoas não estão brincando de besteira.

Usar PhantomReferencetende a ser um pouco mais intrusivo porque você não pode fingir que o getmétodo funciona. Você não pode, por exemplo, escrever um Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference> é realmente um dos locais apropriados para um IdentityHashMap. Observe que uma referência forte manteve a PhantomReference, mas não o referente .

Você realmente quer dizer que para referências fracas, finalize pode recriar o obj? weakref.getpoderia retornar nulle, mais tarde, ainda é capaz de retornar o obj?
Pacerier

@Pacerier finalizenão recria o objeto como tal. Ele pode fazer o objeto fortemente acessível novamente depois de WeakReferenceretornos nullde gete é enfileirado. / (user166390: Como em um mapa digitado no alvo da referência, como WeakHashMapfaz, não um mapa de identidade de referências, o que é bom.)
Tom Hawtin - tackline

1

se você usar o método get (), ele sempre retornará nulo, e não o objeto. [então qual é a utilidade disso]

Os métodos úteis para chamar (em vez de get()) seriam isEnqueued()ou referenceQueue.remove(). Você chamaria esses métodos para executar alguma ação que precisa ocorrer na rodada final da coleta de lixo do objeto.

A primeira vez é quando o objeto tem seu finalize()método chamado, então você pode colocar ganchos de fechamento lá também. No entanto, como outros afirmaram, provavelmente há maneiras mais seguras de realizar a limpeza ou qualquer ação que precise ocorrer antes e depois da coleta de lixo ou, de modo mais geral, no final da vida útil do objeto.


1

Eu encontrei outro uso prático de PhantomReferencesna classe LeakDetector do Jetty.

Jetty usa a LeakDetectorclasse para detectar se o código do cliente adquire um recurso, mas nunca o libera, e a LeakDetectorclasse usa o PhantomReferencespara esse propósito.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.