JavaRush /Java Blogu /Random-AZ /Java-da PhantomReference

Java-da PhantomReference

Qrupda dərc edilmişdir
Salam! Bugünkü dərsimizdə Java-da Phantom References haqqında ətraflı danışacağıq. Bunlar hansı növ əlaqələrdir, niyə "fantom" adlanır və onlardan necə istifadə olunur? Yadınızdadırsa, Java-da 4 növ keçid var:
  1. StrongReference (obyekt yaratarkən yaratdığımız müntəzəm istinadlar):

    Cat cat = new Cat()

    Bu nümunədəki pişik Güclü istinaddır.

  2. SoftReference (yumşaq istinad). Bu keçidlər haqqında mühazirəmiz oldu .

  3. Zəif istinad (zəif istinad). Burada da onlar haqqında mühazirə oxunurdu .

  4. PhantomReference (fantom istinad).

Son üç növün obyektləri yığılır (məsələn, SoftReference<Integer> , WeakReference<MyClass> ). Siniflər SoftReferencevə sinifdən miras alın WeakReference. Bu siniflərlə işləyərkən ən vacib üsullar bunlardır: PhantomReferenceReference
  • get()— bu keçidin istinad etdiyi obyekti qaytarır;

  • clear()— obyektə istinadı silir.

SoftReferenceBu üsulları və haqqında mühazirələrdən xatırlayırsınız WeakReference. Onların müxtəlif növ bağlantılarla fərqli işlədiyini xatırlamaq vacibdir. Bu gün biz ilk üç növü ətraflı nəzərdən keçirməyəcəyik, ancaq fantom əlaqələri haqqında danışacağıq. Biz digər bağlantı növlərinə də toxunacağıq, lakin yalnız fantom bağlantıların onlardan necə fərqləndiyi barədə danışacağımız hissədə. Get! :) Gəlin ilk növbədə fantom keçidlərə niyə ehtiyacımız olduğundan başlayaq. Bildiyiniz kimi, zibil toplayıcısı (Garbage Collector və ya gc) yaddaşı lazımsız Java obyektlərindən azad etmək üçün məsuliyyət daşıyır . Kollektor obyekti iki "keçiddə" çıxarır. İlk keçiddə o, yalnız obyektlərə baxır və lazım gələrsə, onları “lazımsız və silinməli” kimi qeyd edir. Əgər bu obyektin ləğv edilmiş metodu varsa finalize(), o çağırılır. Və ya çağırılmır - şansınızdan asılı olaraq. Yəqin xatırlayırsınız ki, bu, finalize()dəyişkən bir şeydir :) Kollektorun ikinci keçidində obyekt silinir və yaddaş boşalır. Zibil yığanın bu gözlənilməz davranışı bizim üçün bir sıra problemlər yaradır. Zibil yığanın nə vaxt işə başlayacağını dəqiq bilmirik. Metodun çağırılacağını bilmirik finalize(). Üstəlik, əməliyyat zamanı finalize()obyektə güclü bir keçid yaradıla bilər və sonra o, ümumiyyətlə silinməyəcəkdir. Yaddaşa ehtiyacı olan sistemlərdə bu, asanlıqla OutOfMemoryError. Bütün bunlar bizi fantom bağlantılardan istifadə etməyə sövq edir . Məsələ ondadır ki, bu, zibil yığanın davranışını dəyişir. Obyektə yalnız fantom keçidlər qalıbsa, onda o:
  • metod çağırılır finalize()(əgər o, ləğv edilibsə);

  • işdən sonra finalize()heç nə dəyişməyibsə və obyekt hələ də silinə bilərsə, obyektə xəyali istinad xüsusi növbəyə yerləşdirilir - ReferenceQueue.

Fantom istinadlarla işləyərkən başa düşmək lazım olan ən vacib şey odur ki, obyektin fantom istinadı bu növbədə olduğu müddətcə yaddaşdan silinməsin . O, yalnız fantom keçiddən sonra silinəcək clear(). Bir nümunəyə baxaq. Əvvəlcə bəzi məlumatları saxlayacaq test sinfi yaradaq.
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 daha çox yaddaş tutmaq üçün obyektləri yaratarkən (hər obyektə 50 milyon “x” simvolu əlavə etməklə) onları qəsdən məlumatlarla “yükləyirik”. finalize()Bundan əlavə, onun işlədiyini görmək üçün metodu xüsusi olaraq ləğv edirik . Sonra bizə miras alacaq bir sinif lazımdır PhantomReference. Niyə bizə belə bir sinif lazımdır? Bu sadədir. Beləliklə, clear()fantom istinadının həqiqətən təmizləndiyini (və buna görə də obyektin silindiyini) görmək üçün metoda əlavə məntiq əlavə edə bilərik.
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();
   }
}
Sonra, zibil toplayıcının öz işini görməsini gözləyəcək ayrıca bir ipə ehtiyacımız olacaq və ReferenceQueuenövbəmizdə xəyali bağlantılar görünəcək. Belə bir keçid növbəyə daxil olan kimi onun metodu çağırılacaq 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();
   }
}
Və nəhayət, bir üsula ehtiyacımız var main(): gəlin onu ayrı bir sinifə yerləşdirək Main. TestClassOrada bir obyekt , ona fantom istinad və fantom istinadlar üçün növbə yaradacağıq . Bundan sonra zibilyığanı çağıracağıq və görək nə olacaq :)
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 çıxışı: ref = MyPhantomReference@4554617c Növbənin monitorinqi başladı! Zibil toplama adlanır! TestClass obyektində yekunlaşdırma metodu çağırıldı!!! ref = MyPhantomReference@4554617c Zibil kolleksiyası çağırılır! Fantom link təmizlənir! Obyektin yaddaşdan silinməsi! Biz burada nə görürük? Hər şey planlaşdırdığımız kimi oldu! Obyekt sinfimizdə ləğv edilmiş metod var idi finalize()və o, kollektor işləyərkən çağırıldı. Sonra fantom keçid növbəyə qoyuldu ReferenceQueue. Orada onun adlı bir metodu var idi (ondan konsola çıxış əlavə etmək üçün clear()etdik ). cleanup()Nəticədə obyekt yaddaşdan silinib. İndi bunun necə işlədiyini dəqiq görürsünüz :) Təbii ki, fantom keçidlərlə əlaqəli bütün nəzəriyyələri yadda saxlamağa ehtiyac yoxdur. Ancaq heç olmasa əsas məqamları xatırlasanız yaxşı olar. Birincisi , bunlar hamısının ən zəif halqalarıdır. Onlar yalnız obyektə başqa istinadlar olmadıqda işə düşürlər. Yuxarıda verdiyimiz bağlantıların siyahısı gücə görə azalan ardıcıllıqladır: StrongReference-> SoftReference-> WeakReference-> PhantomReference Fantom keçid yalnız obyektimizdə Güclü, Yumşaq və ya Zəif bağlantılar olmadıqda döyüşə girəcək :) İkincisi , metod get()fantom link üçün həmişə qayıdır null. Budur, üç müxtəlif növ avtomobil üçün üç müxtəlif növ əlaqə yaratdığımız sadə bir nümunə:
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 çıxışı: Sedan@4554617c HybridAuto@74a14482 null Metod get()yumşaq keçid və zəif keçid üçün olduqca normal obyektləri qaytardı, lakin nullfantom üçün qaytarıldı. Üçüncüsü , fantom istinadların əsas istifadə sahəsi obyektlərin yaddaşdan çıxarılması üçün mürəkkəb prosedurlardır. Hamısı budur! :) Bununla bugünkü dərsimiz yekunlaşır. Ancaq nəzəriyyə təkbaşına sizi uzağa aparmayacaq, ona görə də problemlərin həllinə qayıtmağın vaxtı gəldi! :)
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION