JavaRush /وبلاگ جاوا /Random-FA /PhantomReference در جاوا

PhantomReference در جاوا

در گروه منتشر شد
سلام! در درس امروز به طور مفصل در مورد مراجع فانتوم در جاوا صحبت خواهیم کرد. اینها چه نوع پیوندهایی هستند، چرا به آنها "فانتوم" می گویند و چگونه از آنها استفاده کنیم؟ همانطور که به یاد دارید، 4 نوع لینک در جاوا وجود دارد:
  1. StrongReference (ارجاعات منظمی که هنگام ایجاد یک شی ایجاد می کنیم):

    Cat cat = new Cat()

    گربه در این مثال یک مرجع قوی است.

  2. SoftReference (مرجع نرم). ما یک سخنرانی در مورد این پیوندها داشتیم.

  3. مرجع ضعیف (مرجع ضعیف). در اینجا یک سخنرانی نیز در مورد آنها برگزار شد .

  4. PhantomReference (مرجع فانتوم).

اشیاء از سه نوع آخر تایپ می شوند (به عنوان مثال، SoftReference<Integer> ، WeakReference<MyClass> ). کلاس ها SoftReferenceو WeakReferenceارث PhantomReferenceبردن از کلاس Reference. مهم ترین روش ها هنگام کار با این کلاس ها عبارتند از:
  • get()- شیئی که این پیوند به آن اشاره دارد را برمی گرداند.

  • clear()- ارجاع به یک شی را حذف می کند.

این روش ها را از سخنرانی هایی در مورد SoftReferenceو WeakReference. مهم است که به یاد داشته باشید که آنها با انواع مختلف پیوندها متفاوت کار می کنند. امروز ما سه نوع اول را با جزئیات در نظر نخواهیم گرفت، بلکه در مورد پیوندهای فانتوم صحبت خواهیم کرد. ما همچنین به انواع دیگر پیوندها خواهیم پرداخت، اما فقط در قسمتی که در مورد تفاوت پیوندهای فانتوم با آنها صحبت خواهیم کرد. برو! :) بیایید با این شروع کنیم که چرا در وهله اول به پیوندهای فانتوم نیاز داریم. همانطور که می دانید زباله جمع کننده (Garbage Collector یا gc) وظیفه آزادسازی حافظه از اشیاء غیر ضروری جاوا را بر عهده دارد . جمع کننده شی را در دو "گذر" حذف می کند. در اولین گذر، فقط به اشیا نگاه می کند و در صورت لزوم، آنها را به عنوان "غیر ضروری و حذف شدنی" علامت گذاری می کند. اگر این شی دارای یک متد نادیده گرفته شده باشد 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 نظارت موضوع صف شروع شده است! جمع آوری زباله نامیده می شود! متد finalize روی شی 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