JavaRush /Java-Blog /Random-DE /PhantomReference in Java

PhantomReference in Java

Veröffentlicht in der Gruppe Random-DE
Hallo! In der heutigen Lektion werden wir ausführlich über Phantomreferenzen in Java sprechen. Was sind das für Links, warum werden sie „Phantom“ genannt und wie verwendet man sie? Wie Sie sich erinnern, gibt es in Java vier Arten von Links:
  1. StrongReference (regelmäßige Referenzen, die wir beim Erstellen eines Objekts erstellen):

    Cat cat = new Cat()

    cat ist in diesem Beispiel eine starke Referenz.

  2. SoftReference (weiche Referenz). Wir hatten einen Vortrag über diese Links.

  3. WeakReference (schwache Referenz). Auch darüber gab es hier einen Vortrag .

  4. PhantomReference (Phantomreferenz).

Objekte der letzten drei Typen werden typisiert (z. B. SoftReference<Integer> , WeakReference<MyClass> ). Klassen SoftReferenceund WeakReferenceerben PhantomReferencevon der Klasse Reference. Die wichtigsten Methoden bei der Arbeit mit diesen Klassen sind:
  • get()– gibt das Objekt zurück, auf das dieser Link verweist;

  • clear()– löscht einen Verweis auf ein Objekt.

Sie kennen diese Methoden aus Vorlesungen über SoftReferenceund WeakReference. Es ist wichtig zu bedenken, dass sie bei verschiedenen Linktypen unterschiedlich funktionieren. Heute werden wir die ersten drei Typen nicht im Detail betrachten, sondern über Phantomlinks sprechen. Wir werden auch auf andere Arten von Links eingehen, jedoch nur in dem Teil, in dem wir darüber sprechen, wie sich Phantomlinks von ihnen unterscheiden. Gehen! :) Beginnen wir damit, warum wir überhaupt Phantomlinks brauchen. Wie Sie wissen, ist der Garbage Collector (Garbage Collector oder GC) dafür verantwortlich, Speicher von unnötigen Java-Objekten freizugeben . Der Sammler entfernt das Objekt in zwei „Durchgängen“. Im ersten Durchgang werden nur Objekte betrachtet und gegebenenfalls als „unnötig und zu löschen“ markiert. Wenn für dieses Objekt eine Methode überschrieben wurde finalize(), wird sie aufgerufen. Oder es wird nicht aufgerufen – abhängig von Ihrem Glück. Sie erinnern sich wahrscheinlich, dass dies finalize()eine launische Sache ist :) Im zweiten Durchgang des Collectors wird das Objekt gelöscht und der Speicher freigegeben. Dieses unvorhersehbare Verhalten des Garbage Collectors stellt uns vor eine Reihe von Problemen. Wir wissen nicht genau, wann der Garbage Collector seine Arbeit aufnehmen wird. Wir wissen nicht, ob die Methode aufgerufen wird finalize(). Außerdem finalize()kann während des Betriebs eine starke Verknüpfung zum Objekt erstellt werden, die dann überhaupt nicht gelöscht wird. Auf speicherhungrigen Systemen kann dies leicht zu OutOfMemoryError. All dies drängt uns dazu, Phantomlinks zu verwenden . Der Punkt ist, dass sich dadurch das Verhalten des Garbage Collectors ändert. Wenn nur noch Phantomlinks zum Objekt vorhanden sind, dann hat es:
  • die Methode wird aufgerufen finalize()(falls sie überschrieben wird);

  • Wenn sich nach der Arbeit finalize()nichts geändert hat und das Objekt noch gelöscht werden kann, wird eine Phantomreferenz auf das Objekt in eine spezielle Warteschlange gestellt – ReferenceQueue.

Beim Arbeiten mit Phantomreferenzen ist es am wichtigsten zu verstehen, dass ein Objekt nicht aus dem Speicher entfernt wird, solange sich seine Phantomreferenz in dieser Warteschlange befindet . Es wird erst gelöscht, nachdem der Phantomlink gelöscht wurde clear(). Schauen wir uns ein Beispiel an. Erstellen wir zunächst eine Testklasse, die einige 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("У ein Objektа TestClass вызван метод finalize!!!");
   }
}
Wir „laden“ Objekte beim Erstellen bewusst mit Daten (fügen jedem Objekt 50 Millionen „x“-Zeichen hinzu), um mehr Speicher zu beanspruchen. Darüber hinaus überschreiben wir die Methode gezielt, finalize()um sicherzustellen, dass sie funktioniert. Als nächstes brauchen wir eine Klasse, die von erbt PhantomReference. Warum brauchen wir eine solche Klasse? Es ist einfach. Auf diese Weise können wir der Methode zusätzliche Logik hinzufügen, clear()um zu sehen, dass die Phantomreferenz tatsächlich gelöscht wurde (und daher 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("Очистка фантомной ссылки! Удаление ein Objektа из памяти!");
       clear();
   }
}
Als nächstes benötigen wir einen separaten Thread, der darauf wartet, dass der Garbage Collector seine Arbeit erledigt, und ReferenceQueuein unserer Warteschlange werden Phantomlinks angezeigt. Sobald ein solcher Link in die Warteschlange gelangt, wird seine Methode aufgerufen cleanup():
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("Поток, отслеживающий очередь, стартовал!");
       Reference ref = null;

       //ждем, пока в очереди появятся ссылки
       while ((ref = referenceQueue.poll()) == null) {

           try {
               Thread.sleep(50);
           }

           catch (InterruptedException e) {
               throw new RuntimeException("Поток " + getName() + " был прерван!");
           }
       }

       //Wie только в очереди появилась фантомная Verknüpfung - очистить ее
       ((MyPhantomReference) ref).cleanup();
   }
}
Und schließlich brauchen wir eine Methode main(): Legen wir sie in eine separate Klasse Main. Darin erstellen wir ein Objekt TestClass, 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("Вызывается сборка мусора!");

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

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

       Thread.sleep(5000);

       System.out.println("Вызывается сборка мусора!");

       System.gc();
   }
}
Konsolenausgabe: ref = MyPhantomReference@4554617c Der Thread, der die Warteschlange überwacht, wurde gestartet! Müllabfuhr heißt! Die finalize-Methode wurde für das TestClass-Objekt aufgerufen!!! ref = MyPhantomReference@4554617c Garbage Collection wird aufgerufen! Bereinigen Sie den Phantomlink! Ein Objekt aus dem Speicher entfernen! Was sehen wir hier? Alles verlief wie geplant! In unserer Objektklasse wurde eine Methode überschrieben finalize(), die aufgerufen wurde, während der Collector ausgeführt wurde. Als nächstes wurde der Phantomlink in die Warteschlange gestellt ReferenceQueue. Dort hatte sie eine Methode namens clear()(von der aus wir cleanup()eine Ausgabe zur Konsole hinzufügten). Infolgedessen wurde das Objekt aus dem Speicher entfernt. Jetzt sehen Sie genau, wie es funktioniert :) Natürlich müssen Sie sich nicht die gesamte Theorie zu Phantomlinks merken. Aber es ist gut, wenn Sie sich zumindest an die wichtigsten Punkte erinnern. Erstens sind dies die schwächsten Glieder von allen. Sie kommen nur ins Spiel, wenn es keine anderen Verweise auf das Objekt gibt. Die Liste der Links, die wir oben angegeben haben, ist in absteigender Reihenfolge ihrer Stärke geordnet: StrongReference-> SoftReference-> WeakReference-> PhantomReference Der Phantomlink tritt nur dann in den Kampf, wenn es keine starken, weichen oder schwachen Links zu unserem Objekt gibt :) Zweitens die Methode get()für Phantom-Link wird immer zurückgegeben null. Hier ist ein einfaches Beispiel, in dem wir drei verschiedene Arten von Links 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 Methode get()hat für den Softlink und den Weak Link ganz normale Objekte zurückgegeben, nullfür den Phantomlink jedoch. Drittens ist der Haupteinsatzbereich von Phantomreferenzen komplexe Verfahren zum Entfernen von Objekten aus dem Speicher. Das ist alles! :) Damit ist unsere Lektion für heute abgeschlossen. Aber mit der Theorie allein kommt man nicht weit, also ist es an der Zeit, sich wieder der Lösung von Problemen zuzuwenden! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION