CodeGym/Blog Java/rawak/Rujukan Phantom di Jawa
John Squirrels
Tahap
San Francisco

Rujukan Phantom di Jawa

Diterbitkan dalam kumpulan
Hai! Dalam perbincangan hari ini, kita akan bercakap secara terperinci tentang "rujukan hantu" (PhantomReference) di Java. Apakah jenis rujukan ini? Mengapa ia dipanggil "rujukan hantu"? Bagaimana ia digunakan? Seperti yang anda ingat, Java mempunyai 4 jenis rujukan:
  1. StrongReference (rujukan biasa yang kami buat semasa mencipta objek):

    Cat cat = new Cat()

    Dalam contoh ini, kucing adalah rujukan yang kuat.

  2. SoftReference (rujukan lembut). Kami mendapat pengajaran tentang rujukan sedemikian.

  3. WeakReference (rujukan lemah). Terdapat juga pengajaran tentang mereka di sini .

  4. PhantomReference (rujukan hantu).

Tiga yang terakhir ialah jenis generik dengan parameter jenis (contohnya, SoftReference<Integer> , WeakReference<MyClass> ). Kelas SoftReference , WeakReference dan PhantomReference diwarisi daripada kelas Reference . Kaedah yang paling penting apabila bekerja dengan kelas ini ialah:
  • get() — mengembalikan objek yang dirujuk;

  • clear() — mengalih keluar rujukan kepada objek.

Anda ingat kaedah ini daripada pelajaran mengenai SoftReference dan WeakReference . Adalah penting untuk diingat bahawa ia berfungsi secara berbeza dengan pelbagai jenis rujukan. Hari ini kita tidak akan menyelami tiga jenis pertama. Sebaliknya, kita akan bercakap tentang rujukan hantu. Kami akan menyentuh jenis rujukan lain, tetapi hanya berkenaan dengan perbezaannya daripada rujukan hantu. Mari pergi! :) Sebagai permulaan, mengapa kita memerlukan rujukan hantu sama sekali? Seperti yang anda ketahui, pengumpul sampah (gc) mengeluarkan memori yang digunakan oleh objek Java yang tidak diperlukan. Pengumpul memadam objek dalam dua "laluan". Dalam laluan pertama, ia hanya melihat objek, dan, jika perlu, menandakannya sebagai "tidak perlu" (bermaksud, "dipadamkan"). Sekiranyafinalize()kaedah telah ditindih untuk objek, ia dipanggil. Atau mungkin ia tidak dipanggil — semuanya bergantung hanya sama ada anda bernasib baik. Anda mungkin ingat itu finalize()berubah-ubah :) Dalam hantaran kedua pemungut sampah, objek dipadamkan dan memori dibebaskan. Tingkah laku pengutip sampah yang tidak dapat diramalkan menimbulkan beberapa masalah kepada kita. Kami tidak tahu dengan tepat bila pemungut sampah akan mula berjalan. Kami tidak tahu sama ada finalize()kaedah itu akan dipanggil. Selain itu, rujukan yang kuat kepada objek boleh dibuat semasa kaedahnya finalize()sedang dilaksanakan, dalam hal ini objek itu tidak akan dipadamkan sama sekali. Untuk program yang membuat permintaan berat pada memori yang tersedia, ini boleh membawa kepada OutOfMemoryError. Semua ini mendorong kita untuk menggunakan rujukan hantu. Hakikatnya ini mengubah tingkah laku pemungut sampah. Jika objek hanya mempunyai rujukan hantu, maka:
  • kaedah finalize() dipanggil (jika ia ditindih)

  • jika tiada apa-apa perubahan setelah kaedah finalize() selesai dan objek masih boleh dipadamkan, maka rujukan hantu kepada objek diletakkan dalam baris gilir khas: ReferenceQueue .

Perkara yang paling penting untuk difahami apabila bekerja dengan rujukan hantu ialah objek tidak dipadamkan daripada ingatan sehingga rujukan hantunya berada dalam baris gilir ini. Ia akan dipadamkan hanya selepas kaedah clear() dipanggil pada rujukan hantu. Mari kita lihat satu contoh. Pertama, kami akan mencipta kelas ujian yang akan menyimpan beberapa jenis 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("The finalize method has been called on the TestClass object");
   }
}
Apabila kami mencipta objek, kami sengaja akan memberi mereka "beban" yang besar (dengan menambahkan 50 juta aksara "x" pada setiap objek) untuk mengambil lebih banyak memori. Di samping itu, kami mengatasi kaedah finalize() untuk melihat bahawa ia dijalankan. Seterusnya, kita memerlukan kelas yang akan diwarisi daripada PhantomReference . Mengapa kita memerlukan kelas sedemikian? Semuanya mudah. Ini akan membolehkan kami menambah logik tambahan pada kaedah clear() untuk mengesahkan bahawa rujukan hantu benar-benar dikosongkan (yang bermaksud objek telah dipadamkan).
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();
   }
}
Seterusnya, kami memerlukan benang berasingan yang akan menunggu pemungut sampah melakukan tugasnya, dan pautan hantu akan muncul dalam ReferenceQueue kami . Sebaik sahaja rujukan sedemikian berakhir dalam baris gilir, kaedah cleanup() dipanggil padanya:
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();
   }
}
Dan akhirnya, kami memerlukan kaedah main() , yang akan kami letakkan dalam kelas Utama yang berasingan . Dalam kaedah itu, kami akan mencipta objek TestClass , rujukan hantu kepadanya dan baris gilir untuk rujukan hantu. Selepas itu, kami akan menghubungi pengutip sampah dan lihat apa yang berlaku :)
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();
   }
}
Output konsol:
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!
Apa yang kita lihat di sini? Semuanya berlaku seperti yang kita rancang! Kaedah finalize() objek kami ditindih dan ia dipanggil semasa pemungut sampah sedang berjalan. Seterusnya, rujukan hantu dimasukkan ke dalam ReferenceQueue . Semasa di sana, kaedah clear() nya dipanggil (di mana kami memanggil cleanup() untuk mengeluarkan ke konsol). Akhirnya, objek telah dipadamkan daripada ingatan. Sekarang anda melihat dengan tepat cara ini berfungsi :) Sudah tentu, anda tidak perlu menghafal semua teori tentang rujukan hantu. Tetapi lebih baik jika anda mengingati sekurang-kurangnya perkara utama. Pertama, ini adalah rujukan yang paling lemah. Mereka mula bermain hanya apabila tiada rujukan lain kepada objek yang ditinggalkan. Senarai rujukan yang kami berikan di atas diisih mengikut tertib menurun daripada yang paling kuat kepada yang paling lemah: StrongReference -> SoftReference -> WeakReference -> PhantomReference Rujukan hantu memasuki pertempuran hanya apabila tiada rujukan yang kuat, lembut atau lemah pada objek kita : ) Kedua, kaedah get() sentiasa mengembalikan null untuk rujukan hantu. Berikut ialah contoh mudah di mana kami mencipta tiga jenis rujukan yang berbeza untuk tiga jenis kereta yang berbeza:
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
Kaedah get() mengembalikan objek biasa sepenuhnya untuk rujukan lembut dan lemah, tetapi ia mengembalikan null untuk rujukan hantu. Ketiga, rujukan hantu digunakan terutamanya dalam prosedur rumit untuk memadam objek daripada ingatan. Itu sahaja! :) Itu mengakhiri pelajaran kita hari ini. Tetapi anda tidak boleh pergi jauh pada teori sahaja, jadi sudah tiba masanya untuk kembali kepada menyelesaikan tugas! :)
Komen
  • Popular
  • Baru
  • Tua
Anda mesti log masuk untuk meninggalkan ulasan
Halaman ini tidak mempunyai sebarang ulasan lagi