JavaRush /مدونة جافا /Random-AR /مرجع فانتوم في جافا

مرجع فانتوم في جافا

نشرت في المجموعة
مرحبًا! في درس اليوم، سنتحدث بالتفصيل عن المراجع الوهمية في لغة جافا. ما نوع هذه الروابط ولماذا يطلق عليها "الوهمية" وكيفية استخدامها؟ كما تتذكر، هناك 4 أنواع من الروابط في Java:
  1. StrongReference (المراجع العادية التي نقوم بإنشائها عند إنشاء كائن):

    Cat cat = new Cat()

    القط في هذا المثال هو مرجع قوي.

  2. مرجع ناعم (مرجع ناعم). كان لدينا محاضرة حول هذه الروابط.

  3. مرجع ضعيف (مرجع ضعيف). وكانت هناك محاضرة عنهم أيضًا هنا .

  4. المرجع الوهمي (المرجع الوهمي).

تتم كتابة الكائنات من الأنواع الثلاثة الأخيرة (على سبيل المثال، SoftReference<Integer> , WeakReference<MyClass> ). الطبقات SoftReference، WeakReferenceويرثون PhantomReferenceمن الطبقة Reference. وأهم الطرق عند العمل مع هذه الفئات هي:
  • get()- إرجاع الكائن الذي يشير إليه هذا الارتباط؛

  • clear()- حذف إشارة إلى كائن.

تتذكر هذه الأساليب من المحاضرات حول SoftReferenceو WeakReference. من المهم أن تتذكر أنها تعمل بشكل مختلف مع أنواع مختلفة من الروابط. واليوم لن نتناول الأنواع الثلاثة الأولى بالتفصيل، بل سنتحدث عن الروابط الوهمية. سنتطرق أيضًا إلى أنواع أخرى من الروابط، ولكن فقط في الجزء الذي سنتحدث فيه عن كيفية اختلاف الروابط الوهمية عنها. يذهب! :) لنبدأ لماذا نحتاج إلى الروابط الوهمية في المقام الأول. كما تعلم، فإن أداة تجميع البيانات المهملة (Garbage Collector أو gc) هي المسؤولة عن تحرير الذاكرة من كائنات Java غير الضرورية . يقوم المجمع بإزالة الكائن في "تمريرتين". في التمريرة الأولى، ينظر فقط إلى الكائنات، وإذا لزم الأمر، يضع علامة عليها باعتبارها "غير ضرورية ويجب حذفها". إذا تم تجاوز طريقة لهذا الكائن 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();
   }
}
إخراج وحدة التحكم: المرجع = 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