JavaRush /Java blogi /Random-UZ /Java-da PhantomReference

Java-da PhantomReference

Guruhda nashr etilgan
Salom! Bugungi darsimizda Java tilidagi Phantom References haqida batafsil gaplashamiz. Bu qanday havolalar, nima uchun ular "fantom" deb ataladi va ulardan qanday foydalanish kerak? Esingizda bo'lsa, Java-da 4 turdagi havolalar mavjud:
  1. StrongReference (ob'ekt yaratishda biz yaratadigan oddiy havolalar):

    Cat cat = new Cat()

    bu misoldagi mushuk kuchli havola.

  2. SoftReference (yumshoq ma'lumotnoma). Biz ushbu havolalar haqida ma'ruza qildik.

  3. Zaif havola (zaif ma'lumotnoma). Bu yerda ular haqida ham ma'ruza bo'ldi .

  4. PhantomReference (hayoliy havola).

Oxirgi uch turdagi ob'ektlar yoziladi (masalan, SoftReference<Integer> , WeakReference<MyClass> ). Sinflar SoftReferenceva sinfdan meros WeakReference. Ushbu sinflar bilan ishlashda eng muhim usullar quyidagilardir: PhantomReferenceReference
  • get()- ushbu havola ko'rsatilgan ob'ektni qaytaradi;

  • clear()— ob'ektga havolani o'chiradi.

SoftReferenceSiz bu usullarni va haqidagi ma'ruzalardan eslaysiz WeakReference. Shuni yodda tutish kerakki, ular turli xil havolalar bilan boshqacha ishlaydi. Bugun biz dastlabki uchta turni batafsil ko'rib chiqmaymiz, lekin xayoliy havolalar haqida gapiramiz. Biz boshqa turdagi havolalarga ham to'xtalib o'tamiz, lekin faqat xayoliy havolalar ulardan qanday farq qilishi haqida gapiradigan qismda. Bor! :) Keling, nima uchun bizga xayoliy havolalar kerakligidan boshlaylik. Ma'lumki, axlat yig'uvchi (Garbage Collector yoki gc) xotirani keraksiz Java ob'ektlaridan bo'shatish uchun javobgardir . Kollektor ob'ektni ikkita "o'tishda" olib tashlaydi. Birinchi o'tishda u faqat ob'ektlarga qaraydi va agar kerak bo'lsa, ularni "keraksiz va o'chirilishi kerak" deb belgilaydi. Agar ushbu ob'ektda bekor qilingan usul bo'lsa finalize(), u chaqiriladi. Yoki chaqirilmaydi - omadingizga qarab. Ehtimol, bu o'zgaruvchan narsa ekanligini eslaysiz finalize():) Kollektorning ikkinchi o'tishida ob'ekt o'chiriladi va xotira bo'shatiladi. Axlat yig'uvchining bu oldindan aytib bo'lmaydigan xatti-harakati biz uchun bir qator muammolarni keltirib chiqaradi. Axlat yig‘uvchi qachon ishlay boshlashini aniq bilmaymiz. Usul chaqiriladimi yoki yo'qligini bilmaymiz finalize(). Bundan tashqari, ish paytida finalize()ob'ektga kuchli havola yaratilishi mumkin va keyin u umuman o'chirilmaydi. Xotiraga chanqoq tizimlarda bu osonlikcha OutOfMemoryError. Bularning barchasi bizni xayoliy havolalardan foydalanishga undaydi . Gap shundaki, bu axlat yig'uvchining xatti-harakatlarini o'zgartiradi. Agar ob'ektga faqat xayoliy havolalar qolsa, unda quyidagilar mavjud:
  • usul chaqiriladi finalize()(agar u bekor qilingan bo'lsa);

  • agar ishdan keyin finalize()hech narsa o'zgarmasa va ob'ekt hali ham o'chirilishi mumkin bo'lsa, ob'ektga xayoliy havola maxsus navbatga qo'yiladi - ReferenceQueue.

Hayoliy havolalar bilan ishlashda tushunish kerak bo'lgan eng muhim narsa shundaki, ob'ekt xotiradan o'chirilmaydi, chunki uning fantom havolasi shu navbatda . U faqat xayoliy havoladan keyin o'chiriladi clear(). Keling, bir misolni ko'rib chiqaylik. Birinchidan, ba'zi ma'lumotlarni saqlaydigan test sinfini yarataylik.
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!!!");
   }
}
Biz ko'proq xotirani egallash uchun ob'ektlarni yaratishda ularni ataylab "yuklaymiz" (har bir ob'ektga 50 million "x" belgisi qo'shamiz). finalize()Bunga qo'shimcha ravishda, u ishlaganligini ko'rish uchun biz usulni maxsus ravishda bekor qilamiz . Keyin bizga meros qilib oladigan sinf kerak PhantomReference. Nega bizga bunday sinf kerak? Hammasi oddiy. clear()Shunday qilib, biz fantom havolasi haqiqatan ham tozalanganligini (va shuning uchun ob'ekt o'chirilgan) ko'rish uchun usulga qo'shimcha mantiq qo'shishimiz mumkin .
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();
   }
}
Keyinchalik, bizga axlat yig'uvchi o'z ishini bajarishini kutadigan alohida ip kerak bo'ladi va ReferenceQueuebizning navbatimizda fantom havolalari paydo bo'ladi. Bunday havola navbatga kirishi bilan uning usuli chaqiriladi 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();
   }
}
Va nihoyat, bizga usul kerak main(): keling, uni alohida sinfga joylashtiramiz Main. TestClassUnda biz ob'ekt , unga xayoliy havola va xayoliy havolalar uchun navbat yaratamiz . Shundan so'ng biz axlat yig'uvchiga qo'ng'iroq qilamiz va nima bo'lishini ko'ramiz :)
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();
   }
}
Konsol chiqishi: ref = MyPhantomReference@4554617c Navbatni kuzatish jarayoni boshlandi! Axlat yig'ish deyiladi! Finalize usuli TestClass obyektida chaqirildi!!! ref = MyPhantomReference@4554617c Chiqindilarni yig'ish chaqirilmoqda! Fantom havolasi tozalanmoqda! Ob'ektni xotiradan olib tashlash! Bu erda nimani ko'ramiz? Hammasi biz rejalashtirganimizdek sodir bo'ldi! Bizning ob'ekt sinfimiz bekor qilingan usulga ega edi finalize()va u kollektor ishlayotgan paytda chaqirildi. Keyinchalik, fantom havolasi navbatga qo'yildi ReferenceQueue. U erda uning usuli bor edi clear()(biz undan cleanup()konsolga chiqish qo'shdik). Natijada ob'ekt xotiradan olib tashlandi. Endi siz uning qanday ishlashini aniq ko'rasiz :) Albatta, fantom havolalar bilan bog'liq barcha nazariyalarni yodlab olishingiz shart emas. Ammo hech bo'lmaganda asosiy fikrlarni eslab qolsangiz yaxshi bo'ladi. Birinchidan , bu eng zaif bo'g'inlardir. Ular faqat ob'ektga boshqa havolalar bo'lmaganda o'ynaydi. Yuqorida biz bergan havolalar ro'yxati kuchning kamayish tartibida: StrongReference-> SoftReference-> WeakReference-> PhantomReference Fantom havola faqat bizning ob'ektimizda Kuchli, Yumshoq yoki Zaif bo'g'inlar bo'lmasa jangga kiradi :) Ikkinchidan , usul get()fantom havola uchun har doim qaytib keladi null. Mana oddiy misol, biz uch xil turdagi avtomobillar uchun uch xil turdagi havolalar yaratamiz:
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());

   }
}
Konsol chiqishi: Sedan@4554617c HybridAuto@74a14482 null Usul get()yumshoq va zaif havola uchun juda oddiy ob'ektlarni qaytardi, lekin nullfantom uchun qaytarildi. Uchinchidan , xayoliy havolalardan foydalanishning asosiy sohasi ob'ektlarni xotiradan olib tashlashning murakkab protseduralari hisoblanadi. Ana xolos! :) Shu bilan bugungi darsimiz yakunlanadi. Ammo nazariyaning o'zi sizni uzoqqa olib bormaydi, shuning uchun muammolarni hal qilishga qaytish vaqti keldi! :)
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION