Phantom Referenzierte Objekte

8

Phantomreferenzen dienen für Post-Mortem-Operationen. Die Java-Spezifikation besagt, dass ein Phantom-referenziertes Objekt erst dann freigegeben wird, wenn die Phantom-Referenz selbst bereinigt wurde.

Meine Frage ist: Welchen Zweck erfüllt dieses Feature (Objekt nicht freigegeben)?

(Die einzige Idee, die ich mir ausgedacht habe, ist, nativen Code zu erlauben, nachträgliche Säuberungen am Objekt durchzuführen, aber das ist nicht sehr überzeugend).

    
Shimi Bandiel 17.09.2008, 07:06
quelle

6 Antworten

1

Der einzige brauchbare Anwendungsfall, der eine Deallokation verhindern würde, ist eine, bei der eine Art von JNI-implementierter asynchroner Datenquelle in das referenzierte Objekt geschrieben wird und angewiesen werden muss, nicht weiter zu schreiben das Objekt - bevor der Speicher recycelt wird. Wenn die vorherige Freigabe aufgehoben wurde, kann ein einfacher Fehler zur Beseitigung () dazu führen, dass der Speicher beschädigt wird.

Dies ist einer der Fälle, in denen finalize () in der Vergangenheit verwendet wurde und wahrscheinlich einige seiner Macken verursacht hat.

    
Ed Staub 13.10.2011, 13:41
quelle
2

Bearbeiten, da ich die Frage zuerst falsch verstanden habe:

Zitat von hier Ссылка :

  

Die Java-Spezifikation besagt, dass die   Phantom-Referenz wird nicht gelöscht, wenn   das Referenzobjekt wird eingereiht, aber   eigentlich gibt es keinen Weg in die   Sprache zu sagen, ob das gewesen ist   getan oder nicht. In einigen Implementierungen   JNI schwache globale Referenzen sind schwächer   als Phantomreferenzen, und a   Zugriff auf Phantom erreichbar   Objekte.

Aber ich fand keine anderen Referenzen, die dasselbe sagen würden.

    
jrudolph 17.09.2008 07:38
quelle
1

Ich denke, die Idee besteht darin, andere Objekte über das hinaus, was das ursprüngliche Objekt tut, zusätzlich zu bereinigen. Wenn das ursprüngliche Objekt beispielsweise nicht erweitert werden kann, um einige Finalisierungselemente zu implementieren, können Sie Phantomreferenzen verwenden.

Das größere Problem ist, dass die JVM keine Garantie dafür gibt, dass ein Objekt jemals finalisiert wird, und ich übernehme im Nachhinein keine Garantie, dass Phantomreferenzen ihr Ding nach der Finalisierung erledigen.

    
Zarkonnen 17.09.2008 07:42
quelle
1
Phantomreferenzen können verwendet werden, um Aktionen vor der Speicherbereinigung auszuführen, z. B. um Ressourcen freizugeben. Stattdessen verwenden Leute normalerweise die Methode finalize (), was keine gute Idee ist. Finalizer haben einen schrecklichen Einfluss auf die Leistung des Garbage Collectors und können die Datenintegrität Ihrer Anwendung beeinträchtigen, wenn Sie nicht sehr vorsichtig sind, da der "Finalizer" in einem zufälligen Thread zu einer zufälligen Zeit aufgerufen wird Im Konstruktor einer Phantomreferenz geben Sie eine ReferenceQueue an, in der die Phantomreferenzen in die Warteschlange eingereiht werden, sobald die referenzierten Objekte "phantomfähig" werden. Phantom erreichbar bedeutet nicht erreichbar außer durch die Phantom-Referenz. Die anfänglich verwirrende Sache ist, dass, obwohl die Phantomreferenz weiterhin das referenzierte Objekt in einem privaten Feld hält (im Gegensatz zu weichen oder schwachen Referenzen), ihre Methode getReference () immer null zurückgibt. Dies ist so, dass Sie das Objekt nicht wieder stark erreichbar machen können Von Zeit zu Zeit können Sie die ReferenceQueue abfragen und prüfen, ob neue PhantomReferences vorhanden sind, deren referenzierte Objekte phantomfähig geworden sind. Um etwas Nützliches zu erreichen, kann man beispielsweise eine Klasse von java.lang.ref.PhantomReference ableiten, die auf Ressourcen verweist, die vor der Garbage Collection freigegeben werden sollten. Das referenzierte Objekt wird nur dann als Garbage Collection behandelt, wenn die Phantomreferenz selbst nicht mehr erreichbar ist.

Ссылка

    
James A. N. Stauffer 17.09.2008 14:52
quelle
0

Dies ist eine perfekte Lösung für APIs, die keinen Lifecycle-Management-Mechanismus haben, die Sie jedoch mit etwas implementieren, das eine explizite Lebenszyklusverwaltung erfordert.

Insbesondere jede Art von API, die nur Objekte im Speicher verwendet, die Sie jedoch mithilfe einer Socket-Verbindung oder Dateiverbindung zu einem anderen, größeren Backing-Speicher neu implementiert haben, kann PhantomReference verwenden, um Verbindungsinformationen zu schließen und zu bereinigen bevor das Objekt gecodiert wird und die Verbindung nie geschlossen wird, weil es keine Lifecycle-Management-API-Schnittstelle gab, die Sie sonst verwenden könnten.

Denken Sie daran, eine einfache Map in eine Datenbank zu verschieben. Wenn die Map-Referenz verworfen wird, gibt es keine explizite "close" -Operation. Wenn Sie jedoch einen Write-Through-Cache implementiert haben, möchten Sie möglicherweise alle Schreibvorgänge abschließen und die Socket-Verbindung zu Ihrer "Datenbank" schließen können.

Unten ist eine Klasse, die ich für diese Art von Sachen verwende. Beachten Sie, dass Verweise auf PhantomReferences nicht lokale Verweise sein müssen, damit sie ordnungsgemäß funktionieren. Andernfalls wird der Jit dazu führen, dass sie vorzeitig in die Warteschlange gestellt werden, bevor Sie die Code-Blöcke verlassen.

%Vor%      * Irgendwo könnte es Aufrufe wie das Folgende geben.      * %Vor%      * Dies definiert eine Implementierung der Holder-Schnittstelle, die gilt      * ein Verweis auf Socket-Objekte. Die Verwendung von trker      * Das obige Objekt könnte dann die Verwendung einer Methode zum Erstellen enthalten      * die Objekte und registrieren Sie die Referenzen wie unten gezeigt.      * %Vor%      * Software, die eine Socket-Verbindung verwenden und diese weitergeben möchte      * Verwenden Sie SocketHolder.get (), um in allen Fällen auf die Socket-Instanz zu verweisen.      * Wenn dann alle SocketHolder-Referenzen gelöscht werden, würde der Socket      * mit der gezeigten Methode released(java.net.Socket) geschlossen werden      * über.      *

     * Die {@link ReferenceTracker} -Klasse verwendet ein {@link PhantomReference} bis zum ersten Argument als      * Der Schlüssel zu einer Map, die einen Verweis auf das zweite Argument enthält. Also, wenn die      * Schlüsselinstanz wird freigegeben, die Schlüsselreferenz wird in die Warteschlange gestellt, aus der sie entfernt werden kann      * Die Warteschlange, und verwendet, um den Wert aus der Karte zu entfernen, an die dann übergeben wird      * freigegeben().      * /     öffentliche abstrakte Klasse ReferenceTracker {         / **          * Die Thread-Instanz, die Einträge aus der Referenzwarteschlange entfernt, wenn sie neu angezeigt werden.          * /         private flüchtige RefQueuePoll Umfrage;         / **          * Die für diese Instanz verwendete Logger-Instanz. Es wird den Namen als Suffix enthalten          * wenn dieser Konstruktor verwendet wird.          * /         private statische finale Logger log = Logger.getLogger (ReferenceTracker.class.getName ());         / **          * Der Name gibt an, um welche Instanz es sich handelt          * Instanzen benötigt.          * /         private final String welcher;         / **          * Erstellt eine neue Instanz von ReferenceTracker unter Verwendung des übergebenen Namens zur Unterscheidung          * Die Instanz in Protokollierung und toString () Implementierung.          * @param which Der Name dieser Instanz für die Unterscheidung mehrerer Instanzen in der Protokollierung usw.          * /         public ReferenceTracker (Zeichenkette which) {             this.which = was;         }         / **          * Erstellt eine neue Instanz von ReferenceTracker ohne qualifizierenden Namen.          * /         public ReferenzTracker () {             this.which = null;         }         / **          * Bietet Zugriff auf den Namen dieser Instanz.          * @return Der Name dieser Instanz.          * /         @Override         öffentliche Zeichenfolge toString () {             if (was == null) {                 return super.toString () + ": ReferenzTracker";             }             return super.toString () + ": ReferenceTracker [" + which + "]";         }         / **          * Unterklassen müssen diese Methode implementieren. Es wird aufgerufen, wenn alle Verweise auf die          * zugeordnete Halterobjekt werden gelöscht.          * @param val Der Wert, der als zweites Argument an einen entsprechenden Aufruf von {@link #trackReference (Object, Object) trackReference (T, K) übergeben wird}          * /         public abstract void freigegeben (K val);         / ** Die Referenz-Queue für Verweise auf die Halter-Objekte * /         private final ReferenceQueuefqueue = new ReferenceQueue ();         / **          * Die Anzahl der Gesamtzahl der Threads, die erstellt und dann als Einträge zerstört wurden          * wurde verfolgt. Wenn es keine verfolgten Verweise gibt, wird keine Warteschlange ausgeführt.          * /         private final AtomicInteger tcnt = neuer AtomicInteger ();         private volatile boolean running;         / **          * Eine Thread-Implementierung, die {@link #refqueue} abfragt, um anschließend {@link freigegeben (K)} aufzurufen          * als Referenzen auf T-Objekte fallengelassen.          * /         private Klasse RefQueuePoll erweitert Thread {             / **              * Die mit dieser Instanz verknüpfte Thread-Nummer.Es könnte kurz zwei Fälle geben              * Diese Klasse existiert in einem flüchtigen System. Wenn dies der Fall ist, wird dieser Wert              * in einigen der Protokollierung sichtbar sein, um die aktiven zu unterscheiden.              * /             private endgültige int mycnt;             / **              * Erstellt eine Instanz dieser Klasse.              * /             öffentliche RefQueuePoll () {                 setDaemon (true);                 setName (getClass (). getName () + ": ReferenzTracker (" + which + ")");                 mycnt = tcnt.incrementAndGet ();             }             / **              * Diese Methode bietet alle Aktivitäten von refqueue.remove()              * ruft und ruft dann released(K) auf, damit die Anwendung die Anwendung freigeben kann              * Ressourcen benötigt.              * /             Öffentlich @Override void run () {                 Versuchen {                     doRun ();                 } catch (Throwable ex) {                     log.log (fertig? Level.INFO: Level.SEVERE,                         ex.toString () + ": phantom ref poll thread stopping", ex);                 } endlich {                     running = false;                 }             }             private volatile boolean done = false;             private void doRun () {                 während (! erledigt) {                     Referenz ref = null;                     Versuchen {                         running = wahr;                         ref = refqueue.remove ();                         K ctl;                         synchronisiert (refmap) {                             ctl = refmap.remove (ref);                             done = actCnt.decrementAndGet () == 0;                             if (log.isLoggable (Level.FINE)) {                                 log.log (Level.FINE, "aktueller Akt refs = {0}, mapsize = {1}", neues Objekt [] {actCnt.get (), refmap.size ()});                             }                             if (actCnt.get ()! = refmap.größe ()) {                                 Throwable ex = new IllegalStateException ("Anzahl der aktiven Referenzen und Kartengröße stimmen nicht überein");                                 log.log (Level.SEVERE, ex.toString (), ex);                             }                         }                         if (log.isLoggable (Level.FINER)) {                             log.log (Level.FINER, "Referenz freigegeben für: {0}, dep = {1}", neues Objekt [] {ref, ctl});                         }                         if (ctl! = null) {                             Versuchen {                                 veröffentlicht (ctl);                                 if (log.isLoggable (Level.FINE)) {                                     log.log (Level.FINE, "abhängiges Objekt freigegeben: {0}", ctl);                                 }                             } catch (LaufzeitException ex) {                                 log.log (Level.SEVERE, ex.toString (), ex);                             }                         }                     } catch (Ausnahme ex) {                         log.log (Level.SEVERE, ex.toString (), ex);                     } endlich {                         if (ref! = null) {                             ref.clear ();                         }                     }                 }                 if (log.isLoggable (Level.FINE)) {                     log.log (Level.FINE, "poll thread {0} shutdown für {1}", neues Objekt [] {mycnt, this});                 }             }         }         / **          * Eine Zählung der aktiven Referenzen.          * /         private final AtomicInteger actCnt = neu AtomicInteger ();         / **          * Zuordnung von T Referenzen zu K Objekten, die für den freigegebenen (K) Aufruf verwendet werden sollen          * /         private final ConcurrentHashMap, K & gt; refmap = neu ConcurrentHashMap, K & gt; ();         / **          * Fügt eine verfolgte Referenz hinzu. dep sollte sich nicht auf ref beziehen, außer möglicherweise          * eine schwacheReferenz. dep ist fast immer etwas, auf das sich ref.          * @throws IllegalArgumentException von ref und dep sind das gleiche Objekt.          * @param dep Das abhängige Objekt, das bereinigt werden muss, wenn ref nicht mehr referenziert wird.          * @param ref das Objekt, dessen Referenz verfolgt werden soll          * /         public void trackReference (T ref, K dep) {             if (ref == dep) {                 Neue IllegalArgumentException auslösen ("Referenziertes Objekt und abhängiges Objekt können nicht identisch sein");             }             PhantomReferenz p = neue PhantomReferenz (ref, refqueue);             synchronisiert (refmap) {                 refmap.put (p, dep);                 if (actCnt.getAndIncrement () == 0 || running == false) {                     if (actCnt.get () & gt; 0 & amp; & amp; running == falsch) {                         if (log.isLoggable (Level.FINE)) {                             log.fine ("stopped phantom ref polling thread");                         }                     }                     poll = neu RefQueuePoll ();                     Umfrage.Anfang();                     if (log.isLoggable (Level.FINE)) {                         log.log (Level.FINE, "Abfragethread # {0} erstellt für {1}", neues Objekt [] {tcnt.get (), this});                     }                 }             }         }         / **          * Diese Methode kann aufgerufen werden, wenn die JVM, in der sich der Tracker befindet, gerade ist          * Herunterfahren oder ein anderer Kontext wird heruntergefahren und die Objekte werden verfolgt          * vom Tracker sollte jetzt freigegeben werden. Diese Methode führt zu          * {@link #released (Object) released (K)} wird für jede ausstehende Referenz aufgerufen.          * /         Öffentliche Void-Shutdown () {             Listrem;             // Kopiere die Werte und lösche die Karte so, dass sie freigegeben wird             // wird nur einmal aufgerufen, wenn GC später Referenzen löscht             synchronisiert (refmap) {                 rem = neue ArrayList (refmap.values ​​());                 refmap.clear ();             }             für (K dep: rem) {                 Versuchen {                     freigegeben (dep);                 } catch (Ausnahme ex) {                     log.log (Level.SEVERE, ex.toString (), ex);                 }             }         }     }

    
Grwww 24.02.2017 15:54
quelle
-2

Sie können zwei Phantom-Caches verwenden, die sehr effizient in der Speicherverwaltung sind. Einfach ausgedrückt, wenn Sie riesige Objekte haben, die teuer zu erstellen sind, aber selten verwendet werden, können Sie einen Phantom-Cache verwenden, um sie zu referenzieren und sicherzustellen, dass sie keinen Speicher verbrauchen, der wertvoller ist. Wenn Sie reguläre Referenzen verwenden, müssen Sie manuell sicherstellen, dass keine Referenzen mehr auf das Objekt vorhanden sind. Sie können dasselbe über jedes Objekt diskutieren, aber Sie müssen die Referenzen in Ihrem Phantom-Cache nicht manuell verwalten. Nur müssen Sie vorsichtig sein, um zu überprüfen, ob sie gesammelt wurden oder nicht.

Sie können auch ein Framework (d. h. eine Factory) verwenden, in dem Referenzen als Phantomreferenzen angegeben werden. Dies ist nützlich, wenn die Objekte viele und kurzlebig sind (d. H. Verwendet und dann entsorgt werden). Sehr praktisch zum Löschen von Speicher, wenn Sie schlampige Programmierer haben, die denken, dass Garbage Collection magisch ist.

    
Javaxpert 17.09.2008 07:40
quelle