CodeGym /مدونة جافا /Random-AR /المراجع الوهمية في جافا
John Squirrels
مستوى
San Francisco

المراجع الوهمية في جافا

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

    Cat cat = new Cat()

    في هذا المثال، تعتبر cat مرجعًا قويًا.

  2. مرجع ناعم (مرجع ناعم). لقد كان لدينا درس حول مثل هذه المراجع.

  3. مرجع ضعيف (مرجع ضعيف). كان هناك أيضًا درس عنهم هنا .

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

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

  • واضح () - يزيل الإشارة إلى الكائن.

تتذكر هذه الطرق من الدروس الموجودة في SoftReference و WeakReference . من المهم أن تتذكر أنها تعمل بشكل مختلف مع أنواع مختلفة من المراجع. اليوم لن نتعمق في الأنواع الثلاثة الأولى. بدلا من ذلك، سنتحدث عن المراجع الوهمية. وسوف نتطرق إلى الأنواع الأخرى من المراجع، ولكن فقط فيما يتعلق بكيفية اختلافها عن المراجع الوهمية. دعنا نذهب! :) في البداية، لماذا نحتاج إلى مراجع وهمية على الإطلاق؟ كما تعلم، يقوم جامع البيانات المهملة (gc) بتحرير الذاكرة المستخدمة بواسطة كائنات Java غير الضرورية. يقوم المجمع بحذف كائن في "تمريرتين". في التمريرة الأولى، ينظر فقط إلى الكائنات، وإذا لزم الأمر، يضع علامة عليها على أنها "غير ضرورية" (أي "سيتم حذفها"). إذا finalize()تم تجاوز الطريقة للكائن، فسيتم استدعاؤها. أو ربما لم يتم استدعاؤه — كل هذا يتوقف فقط على ما إذا كنت محظوظًا. ربما تتذكر أن هذا finalize()أمر متقلب :) في المرور الثاني لأداة تجميع البيانات المهملة، يتم حذف الكائن وتحرير الذاكرة. يخلق السلوك غير المتوقع لجامع البيانات المهملة عددًا من المشكلات بالنسبة لنا. لا نعرف بالضبط متى سيبدأ تشغيل أداة تجميع البيانات المهملة. لا نعرف ما إذا كان finalize()سيتم استدعاء الطريقة أم لا. بالإضافة إلى ذلك، يمكن إنشاء مرجع قوي للكائن أثناء finalize()تنفيذ أسلوبه، وفي هذه الحالة لن يتم حذف الكائن على الإطلاق. بالنسبة للبرامج التي تتطلب متطلبات كبيرة على الذاكرة المتوفرة، يمكن أن يؤدي ذلك بسهولة إلى خطأ OutOfMemoryError. كل هذا يدفعنا إلى استخدام المراجع الوهمية . الحقيقة هي أن هذا يغير سلوك جامع البيانات المهملة. إذا كان الكائن يحتوي على مراجع وهمية فقط، فحينئذٍ:
  • يتم استدعاء أسلوب الإنهاء () الخاص به (إذا تم تجاوزه)

  • إذا لم يتغير شيء بمجرد انتهاء طريقة Finalize() ولا يزال من الممكن حذف الكائن، فسيتم وضع المرجع الوهمي للكائن في قائمة انتظار خاصة: ReferenceQueue .

أهم شيء يجب فهمه عند العمل باستخدام المراجع الوهمية هو أنه لا يتم حذف الكائن من الذاكرة حتى يصبح مرجعه الوهمي في قائمة الانتظار هذه. سيتم حذفه فقط بعد استدعاء الأسلوب الواضح () على المرجع الوهمي. لنلقي نظرة على مثال. أولاً، سنقوم بإنشاء فئة اختبارية لتخزين نوع ما من البيانات.
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("The finalize method has been called on the TestClass object");
   }
}
عندما نقوم بإنشاء كائنات، فإننا نعطيها عمدًا "حملًا" كبيرًا (عن طريق إضافة 50 مليون حرف "x" إلى كل كائن) من أجل استهلاك المزيد من الذاكرة. بالإضافة إلى ذلك، قمنا بتجاوز طريقة الإنهاء () للتأكد من تشغيلها. بعد ذلك، نحتاج إلى فئة ترث من 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("Cleaning up a phantom reference! Removing an object from memory!");
       clear();
   }
}
بعد ذلك، نحتاج إلى سلسلة رسائل منفصلة تنتظر أداة تجميع البيانات المهملة للقيام بعملها، وستظهر الروابط الوهمية في قائمة الانتظار المرجعية الخاصة بنا . بمجرد أن ينتهي هذا المرجع في قائمة الانتظار، يتم استدعاء طريقة التنظيف () عليه:
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("The thread monitoring the queue has started!");
       Reference ref = null;

       // Wait until the references appear in the queue
       while ((ref = referenceQueue.poll()) == null) {

           try {
               Thread.sleep(50);
           }

           catch (InterruptedException e) {
               throw new RuntimeException("Thread " + getName() + " was interrupted!");
           }
       }

       // As soon as a phantom reference appears in the queue, clean it up
       ((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("Collecting garbage!");

       System.gc();
       Thread.sleep(300);

       System.out.println("ref = " + ref);

       Thread.sleep(5000);

       System.out.println("Collecting garbage!");

       System.gc();
   }
}
إخراج وحدة التحكم:

ref = MyPhantomReference@4554617c 
The thread monitoring the queue has started! 
Collecting garbage! 
The finalize method has been called on the TestClass object 
ref = MyPhantomReference@4554617c 
Collecting garbage! 
Cleaning up a phantom reference! 
Removing an object from memory! 
ما الذي نراه هنا؟ كل شيء حدث كما خططنا! تم تجاوز طريقة Finalize() الخاصة بكائننا وتم استدعاؤها أثناء تشغيل أداة تجميع البيانات المهملة. بعد ذلك، تم وضع المرجع الوهمي في قائمة الانتظار المرجعية . أثناء وجوده هناك، تم استدعاء أسلوبه الواضح () (الذي قمنا من خلاله باستدعاء Cleanup () من أجل الإخراج إلى وحدة التحكم). وأخيراً، تم حذف الكائن من الذاكرة. الآن ترى بالضبط كيف يعمل هذا :) بالطبع، لا تحتاج إلى حفظ كل النظريات المتعلقة بالمراجع الوهمية. ولكن سيكون من الجيد أن تتذكر النقاط الرئيسية على الأقل. أولاً، هذه هي أضعف المراجع على الإطلاق. يتم تشغيلها فقط عندما لا يتم ترك أي إشارات أخرى للكائن. قائمة المراجع التي قدمناها أعلاه مرتبة تنازليًا من الأقوى إلى الأضعف: StrongReference -> SoftReference -> WeakReference -> PhantomReference يدخل المرجع الوهمي المعركة فقط في حالة عدم وجود مراجع قوية أو ضعيفة أو ضعيفة لكائننا: ) ثانيًا، تُرجع طريقة get() دائمًا قيمة فارغة للمرجع الوهمي. فيما يلي مثال بسيط حيث نقوم بإنشاء ثلاثة أنواع مختلفة من المراجع لثلاثة أنواع مختلفة من السيارات:
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() كائنات عادية تمامًا للمراجع الضعيفة والضعيفة، لكنها أعادت قيمة فارغة للمرجع الوهمي. ثالثًا، تُستخدم المراجع الوهمية بشكل أساسي في الإجراءات المعقدة لحذف الكائنات من الذاكرة. هذا كل شيء! :) بهذا نختتم درسنا اليوم. لكن لا يمكنك المضي قدمًا في النظرية وحدها، لذا حان الوقت للعودة إلى حل المهام! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION