Hai mai usato PhantomReference in qualche progetto?


90

L'unica cosa che so PhantomReferenceè,

  • Se usi il suo get()metodo, restituirà sempre nulle non l'oggetto. A cosa serve?
  • Usando PhantomReference, ci si assicura che l'oggetto non possa essere resuscitato dal finalizemetodo.

Ma a cosa serve questo concetto / classe?

L'hai mai usato in uno dei tuoi progetti o hai qualche esempio in cui dovremmo usarlo?



Poiché non è possibile ottenere l'oggetto indicato di un PhantomReference, è un termine improprio completo: avrebbe dovuto essere chiamato FakeReferenceo NonReference.
Pacerier

Ecco anothre filo con il codice: stackoverflow.com/q/43311825/632951
Pacerier

Risposte:


47

Ho usato PhantomReferences in un tipo di profiler della memoria semplicistico e molto specializzato per monitorare la creazione e la distruzione degli oggetti. Avevo bisogno di loro per tenere traccia della distruzione. Ma l'approccio è obsoleto. (È stato scritto nel 2004 per J2SE 1.4.) Gli strumenti di profilazione professionale sono molto più potenti e affidabili e le nuove funzionalità di Java 5 come JMX o agenti e JVMTI possono essere utilizzate anche per questo.

PhantomReferences (sempre usati insieme alla coda di riferimento) sono superiori a finalizecui presenta alcuni problemi e dovrebbero quindi essere evitati. Principalmente rendendo gli oggetti nuovamente raggiungibili. Questo potrebbe essere evitato con l'idioma del guardian del finalizzatore (-> leggi di più in "Java efficace"). Quindi sono anche i nuovi finalizzati .

Inoltre, PhantomReferences

consentono di determinare esattamente quando un oggetto è stato rimosso dalla memoria. In effetti sono l'unico modo per determinarlo. Questo non è generalmente così utile, ma potrebbe tornare utile in alcune circostanze molto specifiche come la manipolazione di immagini di grandi dimensioni: se sai per certo che un'immagine dovrebbe essere raccolta in modo spazzatura, puoi aspettare che lo sia effettivamente prima di provare a caricare l'immagine successiva , e quindi rendere meno probabile il temuto OutOfMemoryError. (Citato da enicholas .)

E come ha scritto prima psd , Roedy Green ha una buona sintesi dei riferimenti .


21

Una spiegazione generale della tabella a cubetti , dal glossario Java.

Che ovviamente coincide con la documentazione di PhantomReference :

Oggetti di riferimento fantasma, che vengono accodati dopo che il raccoglitore determina che i loro referenti potrebbero altrimenti essere recuperati. I riferimenti fantasma vengono spesso utilizzati per pianificare le azioni di pulizia pre-mortem in un modo più flessibile di quanto sia possibile con il meccanismo di finalizzazione di Java.

E, ultimo ma non meno importante, tutti i dettagli cruenti ( questa è una buona lettura ): Java Reference Objects (o How I Learned to Stop Worrying and Love OutOfMemoryError) .

Buona codifica. (Ma per rispondere alla domanda, ho usato solo WeakReferences.)


A proposito, una domanda su quell'articolo. Nella sezione su PhantomReference, mantiene un forte riferimento agli oggetti di connessione attraverso queste due tabelle. Significa che le connessioni non diventeranno mai irraggiungibili (supponendo che l'istanza del pool non diventi mai irraggiungibile). Quindi i PhantomReferences corrispondenti non verranno mai accodati, giusto? Oppure mi sfugge qualcosa?
shrini1000

1
Wow, quell'articolo di kdgregory merita un +10
Pacerier

14

Ottima spiegazione dell'utilizzo di Phantom Reference:

I riferimenti fantasma sono un modo sicuro per sapere che un oggetto è stato rimosso dalla memoria. Ad esempio, si consideri un'applicazione che si occupa di immagini di grandi dimensioni. Supponiamo di voler caricare un'immagine grande in memoria quando un'immagine grande è già in memoria che è pronta per la raccolta dei rifiuti. In tal caso, vogliamo attendere che la vecchia immagine venga raccolta prima di caricarne una nuova. Qui, il riferimento fantasma è un'opzione flessibile e sicura da scegliere. Il riferimento della vecchia immagine verrà accodato in ReferenceQueue una volta finalizzato il vecchio oggetto immagine. Dopo aver ricevuto quel riferimento, possiamo caricare la nuova immagine in memoria.


12

Ho trovato un caso d'uso pratico e utile PhantomReferenceche si trova org.apache.commons.io.FileCleaningTrackernel progetto commons-io. FileCleaningTrackercancellerà il file fisico quando il suo oggetto marcatore viene sottoposto a garbage collection.
Qualcosa da prendere in considerazione è la Trackerclasse che estende la PhantomReferenceclasse.


5

QUESTO DOVREBBE ESSERE OBSOLETO CON JAVA 9!
Usa java.util.Cleanerinvece! (O sun.misc.Cleanersul vecchio JRE)

Post originale:


Ho scoperto che l'uso di PhantomReferences ha quasi la stessa quantità di insidie ​​dei metodi finalizer (ma meno problemi una volta che hai capito bene). Ho scritto una piccola soluzione (un framework molto piccolo per utilizzare PhantomReferences) per Java 8. Permette di utilizzare espressioni lambda come callback da eseguire dopo che l'oggetto è stato rimosso. È possibile registrare i callback per le risorse interne che dovrebbero essere chiuse. Con questo ho trovato una soluzione che funziona per me in quanto la rende molto più pratica.

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

Ecco un piccolo esempio per mostrare come viene registrata una richiamata:

  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 poi c'è il metodo ancora più semplice per la chiusura automatica, che fa più o meno come sopra:

this.registerAutoClose(this.resource);

Per rispondere alle tue domande:

[allora a che serve?

Non puoi ripulire qualcosa che non esiste. Ma avrebbe potuto avere risorse che ancora esistono e devono essere ripulite in modo che possano essere rimosse.

Ma a cosa serve questo concetto / classe?

Non è necessariamente per fare qualcosa con alcun effetto diverso dal debug / registrazione. O forse per le statistiche. Lo vedo più come un servizio di notifica dal GC. Potresti anche utilizzarlo per rimuovere i dati aggregati che diventano irrilevanti una volta rimosso l'oggetto (ma probabilmente ci sono soluzioni migliori per questo). Gli esempi spesso menzionano la chiusura delle connessioni al database, ma non vedo come questa sia una buona idea dato che non potresti lavorare con le transazioni. Un framework applicativo fornirà una soluzione molto migliore per questo.

L'hai mai usato in uno dei tuoi progetti o hai qualche esempio in cui dovremmo usarlo? O questo concetto è fatto solo per il punto di vista dell'intervista;)

Lo uso principalmente solo per la registrazione. Quindi posso tracciare gli elementi rimossi e vedere come funziona GC e può essere ottimizzato. Non eseguirò alcun codice critico in questo modo. Se qualcosa deve essere chiuso, allora dovrebbe essere fatto in una dichiarazione di prova con le risorse. E lo uso nei test unitari, per assicurarmi di non avere perdite di memoria. Nello stesso modo in cui lo fa jontejj. Ma la mia soluzione è un po 'più generale.


Sì, proprio come la mia soluzione "java-cleanup". Entrambe sono astrazioni, quindi non è necessario affrontarle direttamente.
Claude Martin

3

Ho usato un PhantomReference in uno unit test per verificare che il codice sotto test non conservasse riferimenti non necessari a qualche oggetto. ( Codice originale )

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 il test :

@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

È comune usare WeakReferencedove PhantomReferenceè più appropriato. Ciò evita alcuni problemi di essere in grado di resuscitare oggetti dopo che un WeakReferenceè stato cancellato / accodato dal garbage collector. Di solito la differenza non ha importanza perché le persone non stanno giocando a stupidi bastardi.

L'uso PhantomReferencetende ad essere un po 'più invadente perché non puoi fingere che il getmetodo funzioni. Non puoi, ad esempio, scrivere un file Phantom[Identity]HashMap.


IdentityHashMap <PhantomReference> è in realtà uno dei luoghi appropriati per IdentityHashMap. Si noti che un forte riferimento è stato mantenuto al PhantomReference, ma non al referente .

Intendi effettivamente dire che per i riferimenti deboli, finalize potrebbe ricreare l'oggetto? weakref.getpotrebbe tornare null, e poi in seguito, è ancora in grado di restituire l'obj?
Pacerier

@Pacerier finalizenon ricrea l'oggetto come tale. Può rendere nuovamente l'oggetto fortemente raggiungibile dopo un WeakReferenceritorno nullda geted è stato accodato. / (user166390: come in una mappa codificata sulla destinazione del riferimento, così come WeakHashMapnon una mappa di identità dei riferimenti che va bene.)
Tom Hawtin - tackline

1

se usi il suo metodo get (), restituirà sempre null e non l'oggetto. [allora a che serve?

I metodi utili per chiamare (piuttosto che get()) sarebbero isEnqueued()o referenceQueue.remove(). Chiameresti questi metodi per eseguire alcune azioni che devono essere eseguite nel round finale della raccolta dei rifiuti dell'oggetto.

La prima volta è quando l'oggetto ha il suo finalize()metodo chiamato, quindi puoi mettere anche gli hook di chiusura lì. Tuttavia, come altri hanno affermato, ci sono probabilmente modi più sicuri per eseguire la pulizia o qualsiasi azione necessaria prima e dopo la raccolta dei rifiuti o, più in generale, al termine del ciclo di vita dell'oggetto.


1

Ho trovato un altro uso pratico di PhantomReferencesnella classe LeakDetector di Jetty.

Jetty utilizza la LeakDetectorclasse per rilevare se il codice client acquisisce una risorsa ma non la rilascia mai e la LeakDetectorclasse utilizza PhantomReferencesper questo scopo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.