JavaRush /Java блогы /Random-KK /Java тіліндегі PhantomReference

Java тіліндегі PhantomReference

Топта жарияланған
Сәлеметсіз бе! Бүгінгі сабақта біз Java тіліндегі Phantom References туралы егжей-тегжейлі сөйлесеміз. Бұл қандай сілтемелер, олар неліктен «фантом» деп аталады және оларды қалай пайдалануға болады? Естеріңізде болса, Java-да сілтемелердің 4 түрі бар:
  1. StrongReference (an objectіні жасау кезінде біз жасайтын тұрақты сілтемелер):

    Cat cat = new Cat()

    бұл мысалдағы мысық күшті сілтеме болып табылады.

  2. SoftReference (жұмсақ анықтама). Біз осы сілтемелер туралы дәріс оқыдық.

  3. Әлсіз сілтеме (әлсіз сілтеме). Мұнда да олар туралы дәріс болды .

  4. PhantomReference (фантомдық сілтеме).

Соңғы үш түрдегі нысандар теріледі (мысалы, SoftReference<Integer> , WeakReference<MyClass> ). Сыныптар SoftReferenceжәне WeakReferenceсыныптан PhantomReferenceмұра Reference. Бұл сыныптармен жұмыс істеудің ең маңызды әдістері:
  • get()- осы сілтеме сілтеме жасайтын нысанды қайтарады;

  • clear()— нысанға сілтемені жояды.

SoftReferenceБұл әдістерді және туралы лекциялардан есте сақтайсыз WeakReference. Олардың әртүрлі сілтеме түрлерімен басқаша жұмыс істейтінін есте ұстаған жөн. Бүгін біз алғашқы үш түрді егжей-тегжейлі қарастырмаймыз, бірақ фантомдық сілтемелер туралы айтатын боламыз. Біз сілтемелердің басқа түрлеріне де тоқталамыз, бірақ фантомдық сілтемелердің олардан айырмашылығы туралы айтатын бөлікте ғана. Бар! :) Алдымен фантомдық сілтемелер не үшін қажет екенін бастайық. Өздеріңіз білетіндей, қоқыс жинағыш (Garbage Collector немесе gc) жадты қажетсіз Java нысандарынан босатуға жауап береді . Коллектор an objectіні екі «өтуде» алып тастайды. Бірінші өту кезінде ол тек нысандарға қарайды және қажет болған жағдайда оларды «қажет емес және жойылуы керек» деп белгілейді. Бұл нысанда қайта анықталған әдіс болса finalize(), ол шақырылады. Немесе ол аталмайды - сәттілікке байланысты. Бұл құбылмалы нәрсе екені есіңізде болса керек finalize():) Коллектордың екінші өтуінде an object жойылып, жады босатылады. Қоқыс жинаушының бұл болжауға келмейтін әрекеті біз үшін бірқатар қиындықтар туғызады. Қоқыс жинаушының нақты қашан жұмысқа кірісетінін білмейміз. Біз әдіс шақырылатынын білмейміз finalize(). Сонымен қатар, жұмыс кезінде finalize()нысанға күшті сілтеме жасалуы мүмкін, содан кейін ол мүлдем жойылмайды. Жадты қажет ететін жүйелерде бұл оңай әкелуі мүмкін OutOfMemoryError. Мұның бәрі бізді фантомдық сілтемелерді пайдалануға итермелейді . Мәселе мынада, бұл қоқыс жинаушының әрекетін өзгертеді. Егер нысанға фантомдық сілтемелер ғана қалса, онда ол бар:
  • әдіс шақырылады finalize()(егер ол қайта белгіленсе);

  • егер жұмыстан кейін finalize()ештеңе өзгермесе және нысанды әлі де жоюға болатын болса, an objectіге фантомдық сілтеме арнайы кезекке қойылады - ReferenceQueue.

Фантомдық сілтемелермен жұмыс істеу кезінде түсіну керек ең маңызды нәрсе - бұл нысанның фантомдық сілтемесі осы кезекте тұрғанша жадтан жойылмайды . Ол фантомдық сілтемеден кейін ғана жойылады clear(). Мысал қарастырайық. Алдымен, кейбір деректерді сақтайтын сынақ сыныбын жасайық.
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!!!");
   }
}
Біз көбірек жадты алу үшін an objectілерді жасау кезінде деректермен әдейі «жүктейміз» (әр нысанға 50 миллион «x» таңбасын қосамыз). finalize()Бұған қоса, біз оның жұмыс істегенін көру үшін әдісті арнайы қайта анықтаймыз . Әрі қарай бізге мұрагер болатын класс қажет PhantomReference. Бізге мұндай сынып не үшін қажет? Бәрі оңай. clear()Осылайша , фантомдық сілтеме шынымен тазартылғанын (сондықтан нысан жойылғанын) көру үшін әдіске қосымша логика қоса аламыз .
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();
   }
}
Әрі қарай, бізге қоқыс жинаушы өз жұмысын жасағанша күтетін бөлек жіп қажет болады және ReferenceQueueбіздің кезекте фантомдық сілтемелер пайда болады. Мұндай сілтеме кезекке кіргеннен кейін оның әдісі келесідей аталады 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();
   }
}
Соңында, бізге әдіс қажет main(): оны бөлек сыныпқа орналастырайық Main. TestClassОнда біз нысанды , оған фантомдық сілтемені және фантомдық сілтемелер үшін кезекті жасаймыз . Осыдан кейін біз қоқыс жинаушыға қоңырау шалып, не болатынын көреміз :)
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();
   }
}
Консоль шығысы: ref = MyPhantomReference@4554617c Кезекті бақылау ағыны басталды! Қоқыс жинау деп аталады! Finalize әдісі TestClass нысанында шақырылды!!! ref = MyPhantomReference@4554617c Қоқыс жинағы шақырылуда! Фантомдық сілтемені тазалау! Объектіні жадтан жою! Мұнда не көріп тұрмыз? Барлығы біз жоспарлағандай болды! Біздің нысан сыныбында қайта анықталған әдіс болды finalize()және ол коллектор жұмыс істеп тұрған кезде шақырылды. Содан кейін фантомдық сілтеме кезекке қойылды ReferenceQueue. Онда оның әдісі бар (біз консольге нәтиже қосу үшін clear()жасадық ). cleanup()Нәтижесінде an object жадтан жойылды. Енді сіз оның қалай жұмыс істейтінін көресіз :) Әрине, фантомдық сілтемелермен байланысты барлық теорияны жаттап алудың қажеті жоқ. Бірақ кем дегенде негізгі ойларды есте сақтасаңыз жақсы болады. Біріншіден , бұл ең әлсіз буындар. Олар нысанға басқа сілтемелер болмаған кезде ғана әрекет етеді. Жоғарыда келтірілген сілтемелер тізімі күшінің кему ретімен берілген: StrongReference-> SoftReference-> WeakReference-> PhantomReference Фантомдық сілтеме біздің нысанда күшті, жұмсақ немесе әлсіз сілтемелер болмаған кезде ғана шайқасқа кіреді :) Екіншіден , әдіс get()үшін фантомдық сілтеме әрқашан қайтарылады null. Міне, біз үш түрлі көлік түріне арналған үш түрлі сілтеме түрін жасайтын қарапайым мысал:
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());

   }
}
Консоль шығысы: Sedan@4554617c HybridAuto@74a14482 null Әдіс get()жұмсақ сілтеме және әлсіз сілтеме үшін қалыпты нысандарды қайтарды, бірақ nullфантом үшін қайтарылды. Үшіншіден , фантомдық сілтемелерді қолданудың негізгі саласы an objectілерді жадтан жоюдың күрделі proceduresалары болып табылады. Осымен болды! :) Осымен бүгінгі сабағымыз аяқталды. Бірақ тек теория сізді алысқа апармайды, сондықтан мәселелерді шешуге қайта оралу уақыты келді! :)
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION