2009-10-21 13 views
69

L'unica cosa che so di PhantomReference è,Hai mai usato PhantomReference in qualsiasi progetto?

  • Se si utilizza il suo metodo get(), sarà sempre tornare null e non l'oggetto. A cosa serve?
  • Utilizzando PhantomReference, si assicura che l'oggetto non possa essere resuscitato dal metodo finalize.

Ma a cosa serve questo concetto/classe?

Avete mai usato questo in uno dei vostri progetti o avete qualche esempio in cui dovremmo usare questo?

+0

Anche https://stackoverflow.com/a/9827686/632951 – Pacerier

+0

Come non è possibile ottenere l'oggetto di riferimento di un PhantomReference, è un termine improprio completo: avrebbe dovuto essere chiamato 'FakeReference' o' NonReference'. – Pacerier

+0

Ecco un altro thread con codice: https://stackoverflow.com/q/43311825/632951 – Pacerier

risposta

42

Ho usato PhantomReference s in un simplistic, very specialized kind of memory profiler per monitorare la creazione e la distruzione dell'oggetto. Avevo bisogno di loro per tenere traccia della distruzione. Ma l'approccio è obsoleto. (È stato scritto nel 2004 con il targeting 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.

PhantomReference s (utilizzato sempre insieme alla coda di riferimento) sono superiori a finalize che presenta alcuni problemi e deve pertanto essere evitato. Realizzando principalmente oggetti nuovamente accessibili. Questo potrebbe essere evitato con l'idioma guardiano del finalizzatore (-> maggiori informazioni in 'Effective Java'). Quindi sono anche il nuovo finalize.

Inoltre, PhantomReference s

consentono di determinare esattamente quando un oggetto è stato rimosso dalla memoria. Loro sono in effetti l'unico modo per determinarlo. Questo non è in genere quello utile, ma potrebbe tornare utile in alcune circostanze molto specifiche come la manipolazione di immagini di grandi dimensioni: se si è certi che un'immagine deve essere raccolta di dati da , è possibile attendere fino a quando non lo è prima di provare a caricare l'immagine successiva, quindi rendere probabile il temuto OutOfMemoryError meno . (Citato da enicholas.)

E come psd scritto prima, Roedy verde ha un good summary of references.

18

A general diced-up table explanation, dal Glossario Java.

che ovviamente coincide con la PhantomReference documentation:

oggetti di riferimento fantasma, che sono accodato dopo il collettore determina che i loro referenti possono altrimenti essere recuperate. 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 Java.

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

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

+0

Btw, 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 (assumendo che l'istanza del pool stesso non diventi mai irraggiungibile). Quindi le corrispondenti PhantomReferences non saranno mai accodate, giusto? O mi sta sfuggendo qualcosa? – shrini1000

+0

Wow, quell'articolo da kdgregory merita un +10 – Pacerier

2

È comune utilizzare WeakReference dove PhantomReference è più appropriato. Ciò evita alcuni problemi di essere in grado di resuscitare gli oggetti dopo che uno viene cancellato/accodato dal garbage collector. Di solito la differenza non ha importanza perché le persone non stanno giocando a stupidi buggers.

L'utilizzo di PhantomReference tende ad essere un po 'più invadente perché non si può pretendere che il metodo get funzioni. Ad esempio, non è possibile scrivere un Phantom[Identity]HashMap.

+0

IdentityHashMap è in realtà uno dei posti appropriati per una IdentityHashMap. Si noti che * riferimento forte * mantenuto su PhantomReference, * ma non sul referente *. –

+0

O l'ho letto completamente sbagliato? Mi scuso se l'ho fatto. –

+0

Intendi dire che per riferimenti deboli, finalize può ricreare l'oggetto? 'weakref.get' potrebbe restituire' null', e successivamente, è ancora in grado di restituire l'oggetto? – Pacerier

9

Ho trovato un caso di utilizzo pratico e utile di PhantomReference che è org.apache.commons.io.FileCleaningTracker nel progetto commons-io. FileCleaningTracker cancellerà il file fisico quando il suo oggetto marcatore è spazzato via.
Qualcosa da prendere in considerazione è la classe Tracker che estende la classe PhantomReference.

3

Ho utilizzato un PhantomReference in un test di unità per verificare che il codice sottoposto a test non abbia mantenuto riferimenti non specifici a determinati oggetti. (Original code)

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>"); 
    } 
} 
7

Great explanation di Phantom utilizzo Riferimento:

riferimenti Phantom sono modo sicuro per conoscere un oggetto è stato rimosso dalla memoria. Ad esempio, considera un'applicazione che si occupa delle immagini grandiose . Supponiamo di voler caricare una grande immagine nella memoria quando l'immagine grande è già in memoria, pronta per la raccolta dei dati inutili raccolta. In tal caso, desideriamo aspettare che l'immagine precedente sia raccolta prima di caricarne una nuova. Qui, il riferimento phantom è opzione flessibile e sicura da scegliere. Il riferimento della vecchia immagine sarà accodato nella ReferenceQueue una volta che il vecchio oggetto immagine è stato finalizzato a . Dopo aver ricevuto tale riferimento, possiamo caricare la nuova immagine nella memoria.

3

QUESTO DEVE ESSERE OBSOLETO CON JAVA 9!
Utilizzare invece java.util.Cleaner! (O sun.misc.Cleaner sui vecchi JRE)

Original post:


ho trovato che l'uso di PhantomReferences ha quasi la stessa quantità di insidie ​​come metodi Finalizer (ma un minor numero di problemi una volta che si ottiene a destra). Ho scritto una piccola soluzione (un framework molto piccolo per usare PhantomReferences) per Java 8. Permette di usare espressioni lambda come callback da eseguire dopo che l'oggetto è stato rimosso. È possibile registrare i callback per le risorse interne che devono essere chiuse. Con questo ho trovato una soluzione che funziona per me in quanto rende molto più pratico.

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

Ecco un piccolo esempio per mostrare come una richiamata è iscritto:

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, facendo più o meno come sopra:

this.registerAutoClose(this.resource); 

per rispondere alle vostre domande:

[allora che cosa è l'uso di esso]

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

Ma a cosa serve questo concetto/classe?

Non è necessariamente necessario eseguire operazioni con effetti diversi dal debug/logging. O forse per le statistiche. Lo vedo più come un servizio di notifica dal GC. Si potrebbe anche volerlo usare per rimuovere dati aggregati che diventano irrilevanti una volta rimosso l'oggetto (ma probabilmente ci sono soluzioni migliori per questo). Gli esempi spesso menzionano le connessioni del database da chiudere, ma non vedo come questa sia una buona idea in quanto non si potrebbe lavorare con le transazioni. Un framework applicativo fornirà una soluzione molto migliore per questo.

Avete mai usato questo in uno dei vostri progetti, o avete qualche esempio in cui dovremmo usare questo? O è questo concetto fatto solo per il punto di vista dell'intervista;)

Lo uso principalmente per la registrazione. Quindi posso rintracciare gli elementi rimossi e vedere come funziona GC e può essere ottimizzato. Non vorrei eseguire alcun codice critico in questo modo. Se qualcosa deve essere chiuso, dovrebbe essere fatto in una dichiarazione try-with-resource. E lo uso nelle unit test, per assicurarmi che non ci siano perdite di memoria. Allo stesso modo di jontejj. Ma la mia soluzione è un po 'più generale.

+0

Bene,' java.util.Cleaner' utilizza internamente PhantomReferences comunque ... – Pacerier

+0

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

1

se si utilizza il metodo get() restituirà sempre null e non l'oggetto . [Allora che cosa è l'uso di esso]

I metodi utili per chiamare (piuttosto che get()) sarebbe isEnqueued() o referenceQueue.remove(). Chiameresti questi metodi per eseguire alcune azioni che devono essere eseguite nell'ultimo ciclo di garbage collection dell'oggetto.

La prima volta è quando l'oggetto ha il suo metodo finalize() chiamato, quindi è possibile inserire anche i ganci di chiusura. Tuttavia, come altri hanno affermato, ci sono probabilmente modi più sicuri di eseguire la pulizia o qualsiasi altra azione deve aver luogo prima e dopo la raccolta dei rifiuti o, più in generale, al termine del ciclo di vita dell'oggetto.

Problemi correlati