CodeGym/Java-Blog/Random-DE/Phantomreferenzen in Java
Autor
Milan Vucic
Programming Tutor at Codementor.io

Phantomreferenzen in Java

Veröffentlicht in der Gruppe Random-DE
Hallo! In der heutigen Diskussion werden wir ausführlich über „Phantomreferenzen“ (PhantomReference) in Java sprechen. Was sind das für Referenzen? Warum werden sie „Phantomreferenzen“ genannt? Wie werden sie verwendet? Wie Sie sich erinnern, gibt es in Java vier Arten von Referenzen:
  1. StrongReference (gewöhnliche Referenzen, die wir beim Erstellen eines Objekts erstellen):

    Cat cat = new Cat()

    In diesem Beispiel ist cat eine starke Referenz.

  2. SoftReference (weiche Referenz). Wir hatten eine Lektion über solche Referenzen.

  3. WeakReference (schwache Referenz). Hier gab es auch eine Lektion darüber .

  4. PhantomReference (Phantomreferenz).

Die letzten drei sind generische Typen mit Typparametern (z. B. SoftReference<Integer> , WeakReference<MyClass> ). Die Klassen SoftReference , WeakReference und PhantomReference werden von der Reference- Klasse geerbt. Die wichtigsten Methoden bei der Arbeit mit diesen Klassen sind:
  • get() – gibt das referenzierte Objekt zurück;

  • clear() – entfernt den Verweis auf das Objekt.

Sie kennen diese Methoden aus den Lektionen zu SoftReference und WeakReference . Es ist wichtig zu bedenken, dass sie bei unterschiedlichen Arten von Referenzen unterschiedlich funktionieren. Auf die ersten drei Typen gehen wir heute nicht näher ein. Stattdessen sprechen wir über Phantomreferenzen. Wir werden auf die anderen Arten von Referenzen eingehen, jedoch nur im Hinblick darauf, wie sie sich von Phantomreferenzen unterscheiden. Lass uns gehen! :) Zunächst einmal: Warum brauchen wir überhaupt Phantomreferenzen? Wie Sie wissen, gibt der Garbage Collector (GC) den von unnötigen Java-Objekten verwendeten Speicher frei. Der Collector löscht ein Objekt in zwei „Durchgängen“. Im ersten Durchgang betrachtet es nur Objekte und markiert sie gegebenenfalls als „unnötig“ (also „zu löschen“). Wenn diefinalize()Wenn die Methode für das Objekt überschrieben wurde, wird sie aufgerufen. Oder vielleicht heißt es auch nicht – es hängt alles nur davon ab, ob Sie Glück haben. Sie erinnern sich wahrscheinlich, dass das finalize()launisch ist :) Im zweiten Durchgang des Garbage Collectors wird das Objekt gelöscht und Speicher freigegeben. Das unvorhersehbare Verhalten des Garbage Collectors stellt uns vor eine Reihe von Problemen. Wir wissen nicht genau, wann der Garbage Collector gestartet wird. Wir wissen nicht, ob die finalize()Methode aufgerufen wird. Außerdem kann ein starker Verweis auf ein Objekt erstellt werden, während seine finalize()Methode ausgeführt wird. In diesem Fall wird das Objekt überhaupt nicht gelöscht. Bei Programmen, die den verfügbaren Speicher stark beanspruchen, kann dies leicht zu einer OutOfMemoryError. All dies veranlasst uns dazu, Phantomreferenzen zu verwenden. Tatsache ist, dass sich dadurch das Verhalten des Garbage Collectors ändert. Wenn das Objekt nur Phantomreferenzen hat, dann:
  • seine finalize()- Methode wird aufgerufen (falls sie überschrieben wird)

  • Wenn sich nach Abschluss der finalize()- Methode nichts ändert und das Objekt weiterhin gelöscht werden kann, wird die Phantomreferenz auf das Objekt in eine spezielle Warteschlange gestellt: ReferenceQueue .

Beim Arbeiten mit Phantomreferenzen ist es am wichtigsten zu verstehen, dass das Objekt erst dann aus dem Speicher gelöscht wird, wenn sich seine Phantomreferenz in dieser Warteschlange befindet. Es wird erst gelöscht, nachdem die Methode „clear()“ für die Phantomreferenz aufgerufen wurde. Schauen wir uns ein Beispiel an. Zuerst erstellen wir eine Testklasse, die Daten speichert.
public class TestClass {
   private StringBuffer data;
   public TestClass() {
       this.data = new StringBuffer();
       for (long i = 0; i < 50000000; i++) {
           this.data.append('x');
       }
   }
   @Override
   protected void finalize() {
       System.out.println("The finalize method has been called on the TestClass object");
   }
}
Wenn wir Objekte erstellen, geben wir ihnen absichtlich eine hohe „Last“ (indem wir jedem Objekt 50 Millionen „x“-Zeichen hinzufügen), um mehr Speicher zu beanspruchen. Darüber hinaus überschreiben wir die Methode finalize(), um sicherzustellen, dass sie ausgeführt wird. Als nächstes benötigen wir eine Klasse, die von PhantomReference erbt . Warum brauchen wir eine solche Klasse? Es ist alles unkompliziert. Dadurch können wir der Methode „clear()“ zusätzliche Logik hinzufügen, um zu überprüfen, ob die Phantomreferenz tatsächlich gelöscht ist (was bedeutet, dass das Objekt gelöscht wurde).
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class MyPhantomReference<TestClass> extends PhantomReference<TestClass> {

   public MyPhantomReference(TestClass obj, ReferenceQueue<TestClass> queue) {

       super(obj, queue);

       Thread thread = new QueueReadingThread<TestClass>(queue);

       thread.start();
   }

   public void cleanup() {
       System.out.println("Cleaning up a phantom reference! Removing an object from memory!");
       clear();
   }
}
Als nächstes benötigen wir einen separaten Thread, der darauf wartet, dass der Garbage Collector seine Arbeit erledigt, und in unserer ReferenceQueue werden Phantomlinks angezeigt . Sobald eine solche Referenz in der Warteschlange landet, wird darauf die Methode cleanup() aufgerufen:
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;

public class QueueReadingThread<TestClass> extends Thread {

   private ReferenceQueue<TestClass> referenceQueue;

   public QueueReadingThread(ReferenceQueue<TestClass> referenceQueue) {
       this.referenceQueue = referenceQueue;
   }

   @Override
   public void run() {

       System.out.println("The thread monitoring the queue has started!");
       Reference ref = null;

       // Wait until the references appear in the queue
       while ((ref = referenceQueue.poll()) == null) {

           try {
               Thread.sleep(50);
           }

           catch (InterruptedException e) {
               throw new RuntimeException("Thread " + getName() + " was interrupted!");
           }
       }

       // As soon as a phantom reference appears in the queue, clean it up
       ((MyPhantomReference) ref).cleanup();
   }
}
Und schließlich benötigen wir die Methode main() , die wir in einer separaten Main- Klasse unterbringen . In dieser Methode erstellen wir ein TestClass- Objekt, eine Phantomreferenz darauf und eine Warteschlange für Phantomreferenzen. Danach rufen wir den Müllsammler an und schauen, was passiert :)
import java.lang.ref.*;

public class Main {

   public static void main(String[] args) throws InterruptedException {
       Thread.sleep(10000);

       ReferenceQueue<TestClass> queue = new ReferenceQueue<>();
       Reference ref = new MyPhantomReference<>(new TestClass(), queue);

       System.out.println("ref = " + ref);

       Thread.sleep(5000);

       System.out.println("Collecting garbage!");

       System.gc();
       Thread.sleep(300);

       System.out.println("ref = " + ref);

       Thread.sleep(5000);

       System.out.println("Collecting garbage!");

       System.gc();
   }
}
Konsolenausgabe:
ref = MyPhantomReference@4554617c
The thread monitoring the queue has started!
Collecting garbage!
The finalize method has been called on the TestClass object
ref = MyPhantomReference@4554617c
Collecting garbage!
Cleaning up a phantom reference!
Removing an object from memory!
Was sehen wir hier? Alles verlief wie geplant! Die finalize()- Methode unseres Objekts wird überschrieben und wurde aufgerufen, während der Garbage Collector ausgeführt wurde. Als nächstes wurde die Phantomreferenz in die ReferenceQueue gestellt . Dort wurde die Methode „clear()“ aufgerufen (in der wir „cleanup()“ aufgerufen haben , um die Ausgabe an die Konsole vorzunehmen). Schließlich wurde das Objekt aus dem Speicher gelöscht. Jetzt sehen Sie genau, wie das funktioniert :) Natürlich müssen Sie sich nicht die gesamte Theorie über Phantomreferenzen merken. Aber es wäre gut, wenn Sie sich zumindest an die wichtigsten Punkte erinnern würden. Erstens sind dies die schwächsten Referenzen von allen. Sie kommen nur ins Spiel, wenn keine anderen Verweise auf das Objekt mehr vorhanden sind. Die Liste der Referenzen, die wir oben angegeben haben, ist in absteigender Reihenfolge vom stärksten zum schwächsten sortiert: StrongReference -> SoftReference -> WeakReference -> PhantomReference Eine Phantomreferenz tritt nur dann in den Kampf ein, wenn es keine starken, weichen oder schwachen Referenzen auf unser Objekt gibt: ) Zweitens gibt die Methode get() für eine Phantomreferenz immer null zurück. Hier ist ein einfaches Beispiel, in dem wir drei verschiedene Arten von Referenzen für drei verschiedene Fahrzeugtypen erstellen:
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;

public class Main {

   public static void main(String[] args) {

       Sedan sedan = new Sedan();
       HybridAuto hybrid = new HybridAuto();
       F1Car f1car = new F1Car();

       SoftReference<Sedan> softReference = new SoftReference<>(sedan);
       System.out.println(softReference.get());

       WeakReference<HybridAuto> weakReference = new WeakReference<>(hybrid);
       System.out.println(weakReference.get());

       ReferenceQueue<F1Car> referenceQueue = new ReferenceQueue<>();

       PhantomReference<F1Car> phantomReference = new PhantomReference<>(f1car, referenceQueue);
       System.out.println(phantomReference.get());

   }
}
Konsolenausgabe:
Sedan@4554617c
HybridAuto@74a14482
null
Die get()- Methode gab für die Soft- und Weak-Referenzen völlig normale Objekte zurück, für die Phantom-Referenz jedoch null . Drittens werden Phantomreferenzen hauptsächlich bei komplizierten Verfahren zum Löschen von Objekten aus dem Speicher verwendet. Das ist es! :) Damit ist unsere heutige Lektion abgeschlossen. Aber mit der Theorie allein kommt man nicht weit, also ist es Zeit, sich wieder dem Lösen von Aufgaben zu widmen! :) :)
Kommentare
  • Beliebt
  • Neu
  • Alt
Du musst angemeldet sein, um einen Kommentar schreiben zu können
Auf dieser Seite gibt es noch keine Kommentare