Domanda

I riferimenti fantasma servono per le operazioni post mortem.La specifica Java afferma che a oggetto di riferimento fantasma non verrà deallocato finché il riferimento fantasma stesso non verrà ripulito.

La mia domanda è:A cosa serve questa funzionalità (oggetto non deallocato)?

(L'unica idea che mi è venuta è quella di consentire al codice nativo di eseguire la pulizia post mortem sull'oggetto, ma non è molto convincente).

È stato utile?

Soluzione

L'unico buon caso d'uso che mi viene in mente, che impedirebbe la deallocazione, è quello in cui una sorta di origine dati asincrona implementata da JNI sta scrivendo nell'oggetto di riferimento e gli deve essere detto di ritirarsi - di smettere di scrivere nell'oggetto - prima che la memoria venga riciclata.Se fosse consentita la deallocazione precedente, un semplice bug forgot-to-dispose() potrebbe causare il danneggiamento della memoria.

Questo è uno dei casi in cui finalize() sarebbe stato utilizzato in passato e probabilmente ne avrebbe causato alcune stranezze.

Altri suggerimenti

Modifica, poiché prima ho frainteso la domanda:

Citato da qui http://www.memorymanagement.org/glossary/p.html:

La specifica Java afferma che il riferimento Phantom non viene cancellato quando l'oggetto di riferimento viene accusato, ma in realtà non c'è modo nella lingua di dire se è stato fatto o meno.In alcune implementazioni, i riferimenti globali deboli di JNI sono più deboli dei riferimenti Phantom e forniscono un modo per accedere agli oggetti raggiungibili Phantom.

Ma non ho trovato altri riferimenti che direbbero la stessa cosa.

Penso che l'idea sia quella di lasciare che altri oggetti eseguano una pulizia extra oltre a ciò che fa l'oggetto originale.Ad esempio, se l'oggetto originale non può essere esteso per implementare alcuni elementi di finalizzazione, è possibile utilizzare riferimenti fantasma.

Il problema più grande è che la JVM non garantisce che un oggetto sarà mai finalizzato, e presumo per estensione che non vi sia alcuna garanzia che i riferimenti fantasma riescano a fare le loro cose dopo la finalizzazione.

I riferimenti fantasma possono essere utilizzati per eseguire azioni di pre-garbage collection come la liberazione di risorse.Invece, le persone di solito usano il metodo finalize() per questo, il che non è una buona idea.I finalizzatori hanno un impatto terribile sulle prestazioni del Garbage Collector e possono compromettere l'integrità dei dati della tua applicazione se non stai molto attento poiché il "finalizzatore" viene richiamato in un thread casuale, in un momento casuale.

Nel costruttore di un riferimento fantasma, si specifica un ReferenceQueue in cui i riferimenti fantasma vengono accodati una volta che gli oggetti a cui viene fatto riferimento diventano "raggiungibili fantasma".Raggiungibile fantasma significa irraggiungibile se non tramite il riferimento fantasma.La cosa inizialmente confusa è che sebbene il riferimento fantasma continui a contenere l'oggetto referenziato in un campo privato (a differenza dei riferimenti soft o deboli), il suo metodo getReference() restituisce sempre null.In questo modo non è possibile rendere nuovamente l'oggetto fortemente raggiungibile.

Di tanto in tanto, puoi eseguire il polling di ReferenceQueue e verificare se sono presenti nuovi PhantomReferences i cui oggetti a cui si fa riferimento sono diventati raggiungibili fantasma.Per poter ottenere qualcosa di utile, si può ad esempio derivare una classe da java.lang.ref.PhantomReference che fa riferimento a risorse che dovrebbero essere liberate prima della garbage collection.L'oggetto a cui si fa riferimento viene sottoposto a Garbage Collection solo quando il riferimento fantasma stesso diventa irraggiungibile.

http://www.javalobby.org/java/forums/m91822870.html#91822413

Si tratta di una soluzione perfetta per le API che non dispongono di un meccanismo di gestione del ciclo di vita, ma che stai implementando con qualcosa che richiede una gestione esplicita del ciclo di vita.

In particolare, qualsiasi tipo di API che utilizzava solo oggetti in memoria, ma che hai reimplementato utilizzando una connessione socket o una connessione file a qualche altro archivio di backup più grande, può utilizzare PhantomReference per "chiudere" e pulire le informazioni di connessione prima del oggetto sottoposto a GC e la connessione non è mai stata chiusa perché non esisteva un'interfaccia API di gestione del ciclo di vita che potresti altrimenti utilizzare.

Pensa a spostare una semplice mappa Map in un database.Quando il riferimento alla mappa viene scartato, non esiste alcuna operazione di "chiusura" esplicita.Tuttavia, se avessi implementato una scrittura tramite cache, ti piacerebbe essere in grado di completare qualsiasi scrittura e chiudere la connessione socket al tuo "database".

Di seguito è riportata una classe che utilizzo per questo tipo di cose.Tieni presente che i riferimenti a PhantomReferences devono essere riferimenti non locali per funzionare correttamente.Altrimenti, il jit li farà accodare prematuramente prima di uscire dai blocchi di codice.

    import java.lang.ref.PhantomReference;
    import java.lang.ref.Reference;
    import java.lang.ref.ReferenceQueue;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.logging.Level;
    import java.util.logging.Logger;

    /**
     * This class provides a way for tracking the loss of reference of one type of
     * object to allow a secondary reference to be used to perform some cleanup
     * activity.  The most common use of this is with one object which might
     * contain or refer to another object that needs some cleanup performed
     * when the referer is no longer referenced.
     * 

* Un esempio potrebbe essere un oggetto di tipi di tipo, che si riferisce o utilizza una connessione socket.Quando il riferimento viene perso, la presa dovrebbe essere * chiusa.Pertanto, potrebbe essere creata un'istanza come in *

     *    ReferenceTracker trker = ReferenceTracker() {
     *        public void released( Socket s ) {
     *            try {
     *                s.close();
     *            } catch( Exception ex ) {
     *                log.log( Level.SEVERE, ex.toString(), ex );
     *            }
     *        }
     *  };
     * 
* Somewhere, there might be calls such as the following. *
     *        interface Holder {
     *            public T get();
     *        }
     *        class SocketHolder implements Holder {
     *            Socket s;
     *            public SocketHolder( Socket sock ) {
     *                s = sock;
     *            }
     *            public Socket get() {
     *                return s;
     *            }
     *        }
     * 
* This defines an implementation of the Holder interface which holds * a reference to Socket objects. The use of the trker * object, above, might then include the use of a method for creating * the objects and registering the references as shown below. *
     *    public SocketHolder connect( String host, int port ) throws IOException {
     *        Socket s = new Socket( host, port );
     *        SocketHolder h = new SocketHolder( s );
     *        trker.trackReference( h, s );
     *        return h;
     *    }
     * 
* Software wishing to use a socket connection, and pass it around would * use SocketHolder.get() to reference the Socket instance, in all cases. * then, when all SocketHolder references are dropped, the socket would * be closed by the released(java.net.Socket) method shown * above. *

* La classe {@link referencetracker} utilizza una {@link Phantomreference} al primo argomento come * La chiave di una mappa che detiene un riferimento al secondo argomento.Pertanto, quando viene rilasciata l'istanza della * chiave, viene messo in coda il riferimento chiave, può essere rimosso dalla coda e utilizzata per rimuovere il valore dalla mappa che viene quindi passata a * rilasciata ().* / ReferenceTracker della classe astratta pubblica { / *** L'istanza del thread che sta rimuovendo le voci dalla coda di riferimento, Refqueue, come appaiono.*/ sondaggio refqueuepoll volatile privato;/*** L'istanza del logger utilizzata per questa istanza.Includerà il nome come suffisso * se viene utilizzato quel costruttore.*/ logger logger finale statico privato = logger.getLogger (referenceTracker.class.getName ());/** * Il nome che indica quale istanza è per la registrazione e altre separazione delle * istanze necessarie.*/ stringa finale privata che;/** * crea una nuova istanza di referencetracker usando il nome passato per differenziare * l'istanza nell'implementazione di registrazione e toString ().* @param which Il nome di questa istanza per la differenziazione di più istanze nella registrazione, ecc.*/ public referenceTracker (stringa che) {this.which = che;} /*** crea una nuova istanza di referencetracker senza nome qualificante.*/ public referCetracker () {this.which = null;} /*** fornisce l'accesso al nome di questa istanza.* @return Il nome di questa istanza.*/ @Override public String tostring () {if (wha == null) {return super.tostring ()+":RiferimentoTracker";} return super.toString ()+":ReferenceTracker["+quale+"]";} /*** Le sottoclassi devono implementare questo metodo.Verrà chiamato quando vengono eliminati tutti i riferimenti all'oggetto del titolare * associato.* @param val Il valore è passato come secondo argomento a una chiamata corrispondente a {@link #TrackReference (Object, Object) TrackReference (T, K)} */ Public Abstract void rilasciato (k Val);/ ** La coda di riferimento per riferimenti agli oggetti del titolare*/ Riferimento finale privato finalEUreFQueue = new ReferenceQueue ();/** * Il conteggio del numero totale di thread che sono stati creati e quindi distrutti quando le voci sono state monitorate.Quando non sono presenti riferimenti tracciati, non è in esecuzione alcuna coda.*/ privato finale atomicinteger tcnt = new atomicinteger ();esecuzione booleana volatile privata;/** * Un'implementazione del thread che sonda {@link #refqueue} per chiamare successivamente {@link rilasciato (k)} * poiché i riferimenti agli oggetti T vengono eliminati.* / Classe privata RefqueuePoll estende il thread { / *** Il numero di thread associato a questa istanza.Potrebbero esserci brevemente due casi di * questa classe che esiste in un sistema volatile.In tal caso, questo valore * sarà visibile in alcune delle registrazioni per differenziare quelli attivi.*/ privato finale int mycnt;/*** Crea un'istanza di questa classe.*/ public refqueuePoll () {setDaemon (true);setNome( getClass().getNome()+":ReferenceTracker ("+quale+")" );miocnt = tcnt.incrementAndGet();} /*** Questo metodo fornisce tutta l'attività dell'esecuzione refqueue.remove()*chiama e poi chiama released(K) per consentire all'applicazione di rilasciare le * risorse necessarie.*/ public @override void run () {try {Dorun ();} catch (Throwable ex) {log.log (fatto?INFORMAZIONI.Livello:Level.Severe, ex.ToString ()+":arresto del thread di poll di riferimento fantasma", ex );} infine {running = false;}} volatile privato booleano done = false;private void dorun () {while (! done) {reference ref = null;provare {running = true;ref = refqueue.remove();Kctl;sincronizzato (refmap) {ctl = refmap.remove (ref);fatto = actCnt.decrementAndGet() == 0;if (log.islogGable (level.fine)) {log.log (level.fine, "Act Act REFS = {0}, mapsize = {1}", nuovo oggetto [] {actcnt.get (), refmap.size ()});} if (actcnt.get ()! = refmap.size ()) {throwable ex = new illegalstateException ("Conte di riferimenti attivi e dimensioni della mappa non sono sincronizzate");log.log(Level.SEVERE, ex.toString(), ex);}} if (log.isloggable (level.finer)) {log.log (level.finer, "Riferimento rilasciato per:{0}, dep={1}", nuovo Oggetto[]{ref, ctl});} if (ctl! = null) {try {rilasciato (ctl);if (log.isloggable (level.fine)) {log.log (level.fine, "Oggetto dipendente rilasciato:{0}", ctl);}} catch (runtimexception ex) {log.log (level.severe, ex.toString (), ex);}}} catch (eccezione ex) {log.log (level.severe, ex.toString (), ex);} infine {if (ref! = null) {ref.clear ();}}} if (log.isloggable (level.fine)) {log.log (level.fine, "Sondaggio thread {0} Shutdown per {1}", nuovo oggetto [] {Mycnt, this});}}} /*** Un conteggio dei riferimenti attivi.*/ privato finale atomicinteger actcnt = new AtomicInteger ();/ ** * Mappa dai riferimenti T agli oggetti K da utilizzare per la chiamata (k) rilasciata */ Private Final ConrrentHashMap, K> refmap = new ConcorrentHashmap, K> ();/*** aggiunge un riferimento tracciato.Il DEP non dovrebbe fare riferimento a Ref in alcun modo se non * una debolezza.dep è quasi sempre qualcosa a cui fa riferimento il rif.* @throws IllegalArgumentException di ref e dep sono lo stesso oggetto.* @param dep L'oggetto dipendente che necessita di pulizia quando non si fa più riferimento a ref.* @param ref l'oggetto il cui riferimento deve essere tracciato */ public void trackReference (t ref, k dep) {if (ref == dep) {lanciare il nuovo illegalargumentException ("oggetto referenziato e oggetto dipendente non può essere lo stesso") ;} Phantomreference p = new Phantomreference (ref, refqueue);sincronizzato (refmap) {refmap.put (p, dep);if (actcnt.getAndInCrement () == 0 || running == false) {if (actcnt.get ()> 0 && running == false) {if (log.isloggable (level.fine)) {log.fine ( "Avvio thread di polling Phantom REF fermato");}} sondaggio = new refqueuepoll ();sondaggio.inizio();if (log.isloggable (level.fine)) {log.log (level.fine, "sondaggio thread #{0} creato per {1}", nuovo oggetto [] {tcnt.get (), this});}}}} /** * Questo metodo può essere chiamato se il JVM in cui si trova il tracker, si sta spegnendo * o qualche altro contesto viene spegnente e gli oggetti tracciati * dal tracker dovrebbero ora essere rilasciati.Questo metodo si tradurrà in * {@link #released (oggetto) rilasciato (k)} chiamato per ogni refernce in sospeso.*/ public void shutdown () {listrem;// Copia i valori e cancella la mappa in modo che il rilascio // sia mai chiamato solo una volta, in caso di evidi GC in seguito Synchronized (refMap) {rem = new ArrayList (refmap.values ​​());refmap.clear();} per (k dep:rem) {try {rilasciato (dep);} catch (eccezione ex) {log.log (level.severe, ex.toString (), ex);}}}}

Può consentire a voi due di avere cache fantasma che sono molto efficienti nella gestione della memoria.In poche parole, se disponi di oggetti enormi che sono costosi da creare ma utilizzati raramente, puoi utilizzare una cache fantasma per fare riferimento ad essi ed essere sicuro che non occupino memoria più preziosa.Se usi riferimenti regolari devi assicurarti manualmente che non siano rimasti riferimenti all'oggetto.Puoi discutere lo stesso su qualsiasi oggetto ma non devi gestire manualmente i riferimenti nella tua cache fantasma.Bisogna solo stare attenti a controllare se sono stati raccolti oppure no.

Inoltre è possibile utilizzare un framework (ad es.una fabbrica) dove i riferimenti sono forniti come riferimenti fantasma.Ciò è utile se gli oggetti sono molti e di breve durata (es.utilizzati e poi smaltiti).Molto utile per liberare la memoria se hai programmatori sciatti che pensano che la garbage collection sia magica.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top