JavaRush /جاوا بلاگ /Random-SD /جاوا ۾ PhantomReference

جاوا ۾ PhantomReference

گروپ ۾ شايع ٿيل
سلام! اڄ جي سبق ۾، اسان جاوا ۾ پريتم حوالن بابت تفصيل سان ڳالهائينداسين. اهي ڪهڙي قسم جون لنڪس آهن، انهن کي ”پريت“ ڇو سڏيو وڃي ٿو ۽ انهن کي ڪيئن استعمال ڪجي؟ جئين توهان کي ياد آهي، جاوا ۾ لنڪ جا 4 قسم آهن:
  1. StrongReference (باقاعده حوالا جيڪي اسان ٺاهيندا آهيون جڏهن اعتراض ٺاهي رهيا آهيون):

    Cat cat = new Cat()

    هن مثال ۾ cat هڪ مضبوط حوالو آهي.

  2. نرم حوالو (نرم حوالو). اسان انهن لنڪن بابت هڪ ليڪچر ڪيو هو .

  3. ڪمزور حوالو (ضعيف حوالو). هتي انهن بابت هڪ ليڪچر پڻ هو .

  4. PhantomReference (پريت جو حوالو).

آخري ٽن قسمن جون شيون ٽائپ ڪيون ويون آهن (مثال طور، SoftReference<Integer> ، WeakReference<MyClass> ). ڪلاس SoftReference، WeakReference۽ PhantomReferenceڪلاس مان ورثي ۾ Reference. انهن طبقن سان ڪم ڪرڻ وقت سڀ کان اهم طريقا آهن:
  • get()- اهو اعتراض واپس ڏئي ٿو جيڪو هن لنڪ ڏانهن اشارو ڪري ٿو؛

  • clear()- هڪ اعتراض جو حوالو حذف ڪري ٿو.

توھان کي اھي طريقا ياد آھن ليڪچرن مان SoftReference۽ WeakReference. اهو ياد رکڻ ضروري آهي ته اهي مختلف قسم جي لنڪ سان مختلف ڪم ڪن ٿا. اڄ اسان تفصيل سان پهرين ٽن قسمن تي غور نه ڪنداسين، پر پريتم لنڪس بابت ڳالهائينداسين. اسان ٻين قسمن جي لنڪس تي پڻ رابطو ڪنداسين، پر صرف ان حصي ۾ جتي اسان ڳالهائينداسين ته ڪيئن پريتم لنڪس انهن کان مختلف آهن. وڃ! :) اچو ته شروع ڪريون ڇو ته اسان کي پهرين جڳهه ۾ پريتم لنڪس جي ضرورت آهي. جئين توهان کي خبر آهي، گاربيج ڪليڪٽر (گاربيج ڪليڪٽر يا جي سي) غير ضروري جاوا شين مان ياداشت کي آزاد ڪرڻ جو ذميوار آهي . ڪليڪٽر اعتراض کي ٻن "پاس" ۾ هٽائي ٿو. پهرين پاس تي، اهو صرف شيون ڏسي ٿو، ۽، جيڪڏهن ضروري هجي ته، انهن کي نشان لڳايو "غير ضروري ۽ ختم ڪيو وڃي." جيڪڏهن هن اعتراض جو طريقو ختم ڪيو ويو آهي finalize()، ان کي سڏيو ويندو آهي. يا اهو نه سڏيو ويو آهي - توهان جي قسمت تي منحصر آهي. توھان کي شايد ياد آھي ته اھو finalize()ھڪڙو ٿڪل شيء آھي :) ڪليڪٽر جي ٻئي پاس ۾، اعتراض ختم ٿي ويو آھي ۽ ياداشت کي آزاد ڪيو ويو آھي. ڪچرو گڏ ڪندڙ جو هي غير متوقع رويو اسان لاءِ ڪيترائي مسئلا پيدا ڪري ٿو. اسان کي خبر ناهي ته ڪچرو گڏ ڪرڻ وارو ڪم ڪڏهن شروع ڪندو. اسان کي خبر ناهي ته اهو طريقو سڏيو ويندو finalize(). ان سان گڏ، آپريشن دوران finalize()اعتراض لاء هڪ مضبوط لنڪ ٺاهي سگهجي ٿو، ۽ پوء ان کي ختم نه ڪيو ويندو. ياداشت جي بکيو سسٽم تي، اهو آساني سان ٿي سگھي ٿو OutOfMemoryError. هي سڀ اسان کي پريتم لنڪس استعمال ڪرڻ جي طرف ڌڪي ٿو . نقطي اهو آهي ته اهو ردي جي ڪليڪٽر جي رويي کي تبديل ڪري ٿو. جيڪڏهن اعتراض ڏانهن صرف پريتم لنڪس رهجي ويا آهن، پوء اهو آهي:
  • طريقو سڏيو ويندو آهي finalize()(جيڪڏهن اهو ختم ٿيل آهي)؛

  • جيڪڏهن ڪم کان پوء finalize()ڪجھ به تبديل نه ڪيو ويو آهي ۽ اعتراض اڃا به ختم ٿي سگهي ٿو، اعتراض لاء هڪ پريتم حوالو هڪ خاص قطار ۾ رکيل آهي - 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!!!");
   }
}
اسان عمدي طور تي "لوڊ" شيون ڊيٽا سان گڏ ٺاهيندا آهيون (هر اعتراض ۾ 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 ٿريڊ مانيٽرنگ قطار شروع ٿي وئي آهي! ڪچرو گڏ ڪرڻ سڏيو ويندو آهي! حتمي طريقي کي سڏيو ويو آهي TestClass اعتراض تي !!! ref = MyPhantomReference@4554617c ڪچرو گڏ ڪرڻ کي سڏيو پيو وڃي! پريتم لنڪ کي صاف ڪرڻ! يادگيري مان ڪنهن شئي کي هٽائڻ! اسان هتي ڇا ٿا ڏسو؟ سڀ ڪجھ ٿيو جيئن اسان رٿابندي ڪئي! اسان جي اعتراض ڪلاس ۾ هڪ طريقو ختم ڪيو ويو هو finalize()۽ ان کي سڏيو ويندو هو جڏهن ڪليڪٽر هلائي رهيو هو. اڳيون، پريتم لنڪ قطار ۾ رکيل هئي ReferenceQueue. اتي هن کي هڪ طريقو سڏيو ويندو هو clear()(جنهن مان اسان cleanup()ڪنسول ۾ آئوٽ شامل ڪيو). نتيجي طور، اعتراض ياداشت مان هٽايو ويو. هاڻي توهان ڏسو ته اهو ڪيئن ڪم ڪري ٿو :) يقينا، توهان کي پريتم لنڪس سان لاڳاپيل سڀني نظريي کي ياد ڪرڻ جي ضرورت ناهي. پر اهو سٺو ٿيندو جيڪڏهن توهان گهٽ ۾ گهٽ مکيه نقطا ياد رکو. پهرين ، اهي سڀ کان ڪمزور ڪڙيون آهن. اهي صرف راند ۾ اچن ٿا جڏهن اعتراض جا ٻيا حوالا نه آهن. لنڪن جي لسٽ جيڪا اسان مٿي ڏني آهي اها طاقت جي نزول جي ترتيب ۾ آهي: 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پريتم لاءِ واپس آيو. ٽيون ، پريت حوالن جي استعمال جو مکيه علائقو ياداشت مان شيون هٽائڻ لاءِ پيچيده طريقا آهن. اهو ئي سڀ ڪجهه آهي! :) هي اسان جي اڄ جي سبق کي ختم ڪري ٿو. پر اڪيلو نظريو توهان کي پري نه ملندو، تنهنڪري اهو وقت آهي مسئلن کي حل ڪرڻ لاء واپس حاصل ڪرڻ جو! :)
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION