JavaRush /Java Blog /Random-ID /Referensi Phantom di Java

Referensi Phantom di Java

Dipublikasikan di grup Random-ID
Halo! Dalam pelajaran hari ini, kita akan membahas secara detail tentang Referensi Phantom di Java. Tautan macam apa ini, mengapa disebut “hantu” dan bagaimana cara menggunakannya? Seperti yang Anda ingat, ada 4 jenis link di Java:
  1. StrongReference (referensi biasa yang kita buat saat membuat objek):

    Cat cat = new Cat()

    cat dalam contoh ini adalah referensi yang kuat.

  2. SoftReference (referensi lunak). Kami mendapat ceramah tentang tautan ini.

  3. WeakReference (referensi lemah). Ada ceramah tentang mereka juga, di sini .

  4. PhantomReference (referensi hantu).

Objek dari tiga tipe terakhir diketik (misalnya, SoftReference<Integer> , WeakReference<MyClass> ). Kelas SoftReference, WeakReferencedan PhantomReferencemewarisi dari kelas Reference. Metode yang paling penting ketika bekerja dengan kelas-kelas ini adalah:
  • get()- mengembalikan objek yang dirujuk tautan ini;

  • clear()— menghapus referensi ke suatu objek.

Anda ingat metode ini dari perkuliahan tentang SoftReferencedan WeakReference. Penting untuk diingat bahwa cara kerjanya berbeda dengan jenis tautan yang berbeda. Hari ini kita tidak akan mempertimbangkan tiga jenis pertama secara rinci, tetapi kita akan berbicara tentang tautan hantu. Kami juga akan membahas jenis tautan lainnya, tetapi hanya di bagian di mana kami akan membahas perbedaan tautan hantu dengan tautan tersebut. Pergi! :) Mari kita mulai dengan alasan mengapa kita memerlukan tautan hantu. Seperti yang Anda ketahui, pengumpul sampah (Garbage Collector atau gc) bertanggung jawab untuk membebaskan memori dari objek Java yang tidak diperlukan . Kolektor memindahkan objek tersebut dalam dua "lintasan". Pada langkah pertama, ia hanya melihat objek, dan, jika perlu, menandainya sebagai “tidak perlu dan harus dihapus.” Jika objek ini memiliki metode yang ditimpa finalize(), maka objek tersebut dipanggil. Atau tidak disebut - tergantung keberuntungan Anda. Anda mungkin ingat bahwa ini finalize()adalah hal yang berubah-ubah :) Pada tahap kedua kolektor, objek dihapus dan memori dibebaskan. Perilaku pemulung yang tidak dapat diprediksi ini menimbulkan sejumlah masalah bagi kami. Kami tidak tahu persis kapan pemulung itu akan mulai bekerja. Kami tidak tahu apakah metode tersebut akan dipanggil finalize(). Plus, selama operasi, finalize()tautan kuat ke objek dapat dibuat, dan kemudian tidak akan dihapus sama sekali. Pada sistem yang haus memori, hal ini dapat dengan mudah menyebabkan OutOfMemoryError. Semua ini mendorong kita untuk menggunakan tautan hantu . Intinya hal ini mengubah perilaku pemulung. Jika hanya ada link bayangan yang tersisa ke objek, maka objek tersebut memiliki:
  • metode ini dipanggil finalize()(jika ditimpa);

  • jika setelah bekerja finalize()tidak ada yang berubah dan objek masih dapat dihapus, referensi bayangan ke objek ditempatkan dalam antrian khusus - ReferenceQueue.

Hal yang paling penting untuk dipahami ketika bekerja dengan referensi bayangan adalah bahwa suatu objek tidak dihapus dari memori selama referensi bayangannya berada dalam antrian ini . Itu akan dihapus hanya setelah tautan hantu itu clear(). Mari kita lihat sebuah contoh. Pertama, mari buat kelas pengujian yang akan menyimpan beberapa data.
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("У an object TestClass вызван метод finalize!!!");
   }
}
Kami sengaja “memuat” objek dengan data saat membuatnya (menambahkan 50 juta karakter “x” ke setiap objek) untuk menggunakan lebih banyak memori. Selain itu, kami secara khusus mengganti metode tersebut finalize()untuk memastikan metode tersebut berhasil. Selanjutnya kita membutuhkan kelas yang akan diwarisi dari PhantomReference. Mengapa kita membutuhkan kelas seperti itu? Itu mudah. Dengan cara ini kita dapat menambahkan logika tambahan ke metode clear()untuk melihat bahwa referensi hantu sebenarnya telah dihapus (dan oleh karena itu objek telah dihapus).
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("Очистка фантомной ссылки! Удаление an object из памяти!");
       clear();
   }
}
Selanjutnya, kita memerlukan thread terpisah yang akan menunggu pengumpul sampah melakukan tugasnya, dan ReferenceQueuelink hantu akan muncul di antrian kita. Segera setelah tautan tersebut masuk ke antrian, metodenya akan dipanggil 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() + " был прерван!");
           }
       }

       //How только в очереди появилась фантомная link - очистить ее
       ((MyPhantomReference) ref).cleanup();
   }
}
Dan terakhir, kita memerlukan sebuah metode main(): mari kita letakkan di kelas terpisah Main. Di dalamnya kita akan membuat sebuah objek TestClass, referensi bayangan padanya dan antrian untuk referensi bayangan. Setelah itu kami akan memanggil pemulung dan melihat apa yang terjadi :)
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();
   }
}
Output konsol: ref = MyPhantomReference@4554617c Thread yang memantau antrian telah dimulai! Pengumpulan sampah disebut! Metode finalisasi telah dipanggil pada objek TestClass!!! ref = MyPhantomReference@4554617c Pengumpulan sampah sedang dipanggil! Membersihkan tautan hantu! Menghapus objek dari memori! Apa yang kita lihat di sini? Semuanya terjadi sesuai rencana kami! Kelas objek kami memiliki metode yang diganti finalize()dan dipanggil saat kolektor sedang berjalan. Selanjutnya, tautan hantu dimasukkan ke dalam antrean ReferenceQueue. Di sana dia memanggil metode clear()(dari mana kami cleanup()menambahkan output ke konsol). Akibatnya, objek tersebut terhapus dari memori. Sekarang Anda tahu persis cara kerjanya :) Tentu saja, Anda tidak perlu menghafal semua teori yang terkait dengan tautan hantu. Namun alangkah baiknya jika Anda mengingat setidaknya poin utamanya. Pertama , ini adalah mata rantai yang paling lemah. Mereka ikut berperan hanya jika tidak ada referensi lain terhadap objek tersebut. Daftar tautan yang kami berikan di atas adalah dalam urutan kekuatan: StrongReference-> SoftReference-> WeakReference-> PhantomReference Tautan hantu akan masuk ke dalam pertempuran hanya ketika tidak ada tautan Kuat, Lunak, atau Lemah ke objek kami :) Kedua , metode get()untuk tautan hantu selalu kembali null. Berikut adalah contoh sederhana di mana kita membuat tiga jenis tautan berbeda untuk tiga jenis mobil berbeda:
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());

   }
}
Output konsol: Sedan@4554617c HybridAuto@74a14482 null Metode ini get()mengembalikan objek yang cukup normal untuk tautan lunak dan tautan lemah, namun dikembalikan nulluntuk objek hantu. Ketiga , area utama penggunaan referensi hantu adalah prosedur kompleks untuk menghapus objek dari memori. Itu saja! :) Ini menyimpulkan pelajaran kita hari ini. Namun teori saja tidak akan membawa Anda sejauh ini, jadi inilah saatnya kembali memecahkan masalah! :)
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION