Haben Sie jemals PhantomReference in einem Projekt verwendet?

89

Das einzige was ich weiß PhantomReferenceist:

  • Wenn Sie die get()Methode verwenden, wird immer nulldas Objekt zurückgegeben und nicht das Objekt. Was nützt es?
  • Durch die Verwendung PhantomReferencestellen Sie sicher, dass das Objekt nicht von der finalizeMethode wiederbelebt werden kann.

Aber wozu dient dieses Konzept / diese Klasse?

Haben Sie dies jemals in einem Ihrer Projekte verwendet oder haben Sie ein Beispiel, wo wir dies verwenden sollten?

Rakesh Juyal
quelle
Da Sie das verwiesene Objekt einer PhantomReferenz nicht erhalten können, ist es eine völlige Fehlbezeichnung: Es hätte aufgerufen werden sollen FakeReferenceoder NonReference.
Pacerier
Hier ist ein weiterer Thread mit Code: stackoverflow.com/q/43311825/632951
Pacerier

Antworten:

47

Ich habe PhantomReferences in a verwendet simplen, sehr speziellen Speicherprofiler verwendet , um die Objekterstellung und -zerstörung zu überwachen. Ich brauchte sie, um die Zerstörung im Auge zu behalten. Der Ansatz ist jedoch veraltet. (Es wurde 2004 für J2SE 1.4 geschrieben.) Professionelle Profiling-Tools sind viel leistungsfähiger und zuverlässiger, und die neueren Java 5-Funktionen wie JMX oder Agenten und JVMTI können auch dafür verwendet werden.

PhantomReferences (immer zusammen mit der Referenzwarteschlange verwendet) sind überlegen, finalizehaben einige Probleme und sollten daher vermieden werden. Hauptsächlich Objekte wieder erreichbar machen. Dies könnte mit der Finalizer-Guardian-Sprache vermieden werden (-> lesen Sie mehr in 'Effective Java'). Sie sind also auch das neue Finale .

Darüber hinaus PhantomReferences

Mit dieser Option können Sie genau bestimmen, wann ein Objekt aus dem Speicher entfernt wurde. Sie sind in der Tat der einzige Weg, dies festzustellen. Dies ist im Allgemeinen nicht so nützlich, kann jedoch unter bestimmten Umständen nützlich sein, z. B. beim Bearbeiten großer Bilder: Wenn Sie sicher sind, dass ein Bild durch Müll gesammelt werden sollte, können Sie warten, bis es tatsächlich vorhanden ist, bevor Sie versuchen, das nächste Bild zu laden und machen daher den gefürchteten OutOfMemoryError weniger wahrscheinlich. (Zitiert aus Enicholas .)

Und wie psd zuerst schrieb, hat Roedy Green eine gute Zusammenfassung der Referenzen .

Peter Kofler
quelle
21

Eine allgemeine Erklärung der Würfeltabelle aus dem Java-Glossar.

Was natürlich mit der PhantomReference-Dokumentation übereinstimmt :

Phantomreferenzobjekte, die in die Warteschlange gestellt werden, nachdem der Kollektor festgestellt hat, dass ihre Referenten anderweitig zurückgefordert werden können. Phantomreferenzen werden am häufigsten verwendet, um Pre-Mortem-Bereinigungsaktionen flexibler zu planen, als dies mit dem Java-Finalisierungsmechanismus möglich ist.

Und zu guter Letzt alle wichtigen Details ( dies ist eine gute Lektüre ): Java-Referenzobjekte (oder wie ich gelernt habe, mich nicht mehr zu sorgen und OutOfMemoryError zu lieben) .

Viel Spaß beim Codieren. (Aber um die Frage zu beantworten, habe ich immer nur WeakReferences verwendet.)


quelle
Übrigens eine Frage zu diesem Artikel. Im Abschnitt über PhantomReference verweist er in diesen beiden Tabellen stark auf Verbindungsobjekte. Dies bedeutet, dass die Verbindungen niemals nicht mehr erreichbar sind (vorausgesetzt, die Poolinstanz selbst wird niemals nicht mehr erreichbar sein). Die entsprechenden PhantomReferenzen werden also niemals in die Warteschlange gestellt, oder? Oder fehlt mir etwas?
shrini1000
1
Wow, dieser Artikel von kdgregory verdient einen +10
Pacerier
14

Gute Erklärung für die Verwendung der Phantomreferenz:

Phantomreferenzen sind eine sichere Methode, um zu erkennen, dass ein Objekt aus dem Speicher entfernt wurde. Stellen Sie sich beispielsweise eine Anwendung vor, die sich mit großen Bildern befasst. Angenommen, wir möchten ein großes Bild in den Speicher laden, wenn sich bereits ein großes Bild im Speicher befindet, der für den gesammelten Müll bereit ist. In diesem Fall möchten wir warten, bis das alte Bild erfasst ist, bevor wir ein neues laden. Hier ist die Phantomreferenz flexibel und sicher wählbar. Die Referenz des alten Bildes wird in die ReferenceQueue eingereiht, sobald das alte Bildobjekt fertiggestellt ist. Nachdem wir diese Referenz erhalten haben, können wir das neue Bild in den Speicher laden.

Sergii Shevchyk
quelle
12

Ich fand einen praktischen und nützlichen Anwendungsfall von PhantomReferencedenen org.apache.commons.io.FileCleaningTrackerin commons-io - Projekt. FileCleaningTrackerlöscht die physische Datei, wenn das Markierungsobjekt durch Müll gesammelt wird.
Zu beachten ist die TrackerKlasse, die die Klasse erweitert PhantomReference.

Tan Hui Onn
quelle
5

DAS SOLLTE MIT JAVA 9 OBSOLET SEIN!
Verwenden Sie java.util.Cleanerstattdessen! (Oder sun.misc.Cleanerauf älteren JRE)

Ursprünglicher Beitrag:


Ich habe festgestellt, dass die Verwendung von PhantomReferences fast genauso viele Fallstricke aufweist wie die Finalizer-Methoden (aber weniger Probleme, sobald Sie es richtig verstanden haben). Ich habe eine kleine Lösung (ein sehr kleines Framework zur Verwendung von PhantomReferences) für Java 8 geschrieben. Sie ermöglicht die Verwendung von Lambda-Ausdrücken als Rückrufe, die ausgeführt werden, nachdem das Objekt entfernt wurde. Sie können die Rückrufe für innere Ressourcen registrieren, die geschlossen werden sollen. Damit habe ich eine Lösung gefunden, die für mich funktioniert, da sie viel praktischer ist.

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

Hier ist ein kleines Beispiel, um zu zeigen, wie ein Rückruf registriert wird:

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

Und dann gibt es die noch einfachere Methode zum automatischen Schließen, die ungefähr das Gleiche wie oben tut:

this.registerAutoClose(this.resource);

So beantworten Sie Ihre Fragen:

[was nützt es dann]

Sie können etwas, das nicht existiert, nicht aufräumen. Aber es könnte Ressourcen gegeben haben, die noch vorhanden sind und bereinigt werden müssen, damit sie entfernt werden können.

Aber wozu dient dieses Konzept / diese Klasse?

Es ist nicht unbedingt erforderlich, etwas anderes als das Debuggen / Protokollieren zu tun. Oder vielleicht für Statistiken. Ich sehe es eher wie einen Benachrichtigungsdienst vom GC. Sie können es auch verwenden, um aggregierte Daten zu entfernen, die nach dem Entfernen des Objekts irrelevant werden (aber es gibt wahrscheinlich bessere Lösungen dafür). Beispiele erwähnen oft, dass Datenbankverbindungen geschlossen werden müssen, aber ich sehe nicht, dass dies eine so gute Idee ist, da Sie nicht mit Transaktionen arbeiten konnten. Ein Anwendungsframework bietet dafür eine viel bessere Lösung.

Haben Sie dies jemals in einem Ihrer Projekte verwendet oder haben Sie ein Beispiel, wo wir dies verwenden sollten? Oder ist dieses Konzept nur aus Interviewgründen gemacht;)

Ich benutze es meistens nur zum Protokollieren. So kann ich die entfernten Elemente verfolgen und sehen, wie GC funktioniert und optimiert werden kann. Ich würde auf diese Weise keinen kritischen Code ausführen. Wenn etwas geschlossen werden muss, sollte dies in einer Try-with-Resource-Anweisung erfolgen. Und ich benutze es in Unit-Tests, um sicherzustellen, dass ich keine Speicherlecks habe. Genauso wie es Jontejj tut. Aber meine Lösung ist etwas allgemeiner.

Claude Martin
quelle
Ja, genau wie meine Lösung "Java-Cleanup". Beide sind Abstraktionen, sodass Sie sich nicht direkt mit ihnen befassen müssen.
Claude Martin
3

Ich habe eine PhantomReferenz in einem Komponententest verwendet, um zu überprüfen, ob der zu testende Code keine unnötigen Verweise auf ein Objekt enthält. ( Originalcode )

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

Und der 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>");
    }
}
jontejj
quelle
2

Es ist üblich, dort zu verwenden, WeakReferencewo PhantomReferencees angemessener ist. Dies vermeidet bestimmte Probleme, Objekte nach a wiederbeleben zu könnenWeakReference vom Garbage Collector gelöscht / in die Warteschlange gestellt wurde. Normalerweise spielt der Unterschied keine Rolle, weil die Leute keine dummen Kerle spielen.

Die Verwendung ist PhantomReferencein der Regel etwas aufdringlicher, da Sie nicht so tun können, als ob die getMethode funktioniert. Sie können zum Beispiel nicht schreiben a Phantom[Identity]HashMap.

Tom Hawtin - Tackline
quelle
IdentityHashMap <PhantomReference> ist tatsächlich einer der geeigneten Orte für eine IdentityHashMap. Beachten Sie, dass ein starker Verweis auf die PhantomReference beibehalten wird , nicht jedoch auf den Referenten .
Meinen Sie tatsächlich, dass bei schwachen Referenzen finalize das Objekt neu erstellen kann? weakref.getkönnte zurückkehren null, und dann später ist es immer noch in der Lage, das Objekt zurückzugeben?
Pacerier
@Pacerier finalizeerstellt das Objekt nicht als solches neu. Es kann das Objekt nach einer WeakReferenceRückkehr nullvon wieder stark erreichbar machen getund in die Warteschlange stellen. / (user166390: Wie in einer Karte, die auf dem Ziel der Referenz eingegeben wurde, ebenso WeakHashMapwie eine Identitätskarte mit Referenzen, die in Ordnung ist.)
Tom Hawtin - Tackline
1

Wenn Sie die Methode get () verwenden, wird immer null und nicht das Objekt zurückgegeben. [was nützt es dann]

Die nützlichen Methoden zum Aufrufen (anstatt get()) wären isEnqueued()oder referenceQueue.remove(). Sie würden diese Methoden aufrufen, um eine Aktion auszuführen, die in der letzten Runde der Speicherbereinigung des Objekts ausgeführt werden muss.

Das erste Mal wird die finalize()Methode des Objekts aufgerufen, sodass Sie dort auch schließende Hooks platzieren können. Wie andere bereits festgestellt haben, gibt es wahrscheinlich sicherere Methoden zur Durchführung von Bereinigungen oder Maßnahmen, die vor und nach der Speicherbereinigung oder allgemeiner nach dem Ende der Lebensdauer des Objekts erforderlich sind.


quelle
1

Ich fand eine andere praktische Verwendung PhantomReferencesin der LeakDetector- Klasse von Jetty.

Jetty verwendet die LeakDetectorKlasse, um festzustellen, ob der Clientcode eine Ressource erfasst, diese jedoch nie freigibt, und die LeakDetectorKlasse verwendet die PhantomReferenceszu diesem Zweck.

Sahil Chhabra
quelle