CodeGym /Java Blog /এলোমেলো /জাভাতে ফ্যান্টম রেফারেন্স
John Squirrels
লেভেল 41
San Francisco

জাভাতে ফ্যান্টম রেফারেন্স

এলোমেলো দলে প্রকাশিত
ওহে! আজকের আলোচনায়, আমরা জাভাতে "ফ্যান্টম রেফারেন্স" (ফ্যান্টম রেফারেন্স) সম্পর্কে বিস্তারিত কথা বলব। এই ধরনের রেফারেন্স কি? কেন তাদের "ফ্যান্টম রেফারেন্স" বলা হয়? তারা কিভাবে ব্যবহার করা হয়? আপনি যেমন মনে রাখবেন, জাভাতে 4 ধরণের রেফারেন্স রয়েছে:
  1. StrongReference (সাধারণ রেফারেন্স যা আমরা একটি বস্তু তৈরি করার সময় তৈরি করি):

    Cat cat = new Cat()

    এই উদাহরণে, বিড়াল একটি শক্তিশালী রেফারেন্স।

  2. SoftReference (সফট রেফারেন্স)। আমরা যেমন রেফারেন্স সম্পর্কে একটি পাঠ ছিল.

  3. দুর্বল রেফারেন্স (দুর্বল রেফারেন্স)। এখানে তাদের সম্পর্কে একটি পাঠ ছিল ।

  4. ফ্যান্টম রেফারেন্স (ফ্যান্টম রেফারেন্স)।

শেষ তিনটি হল টাইপ প্যারামিটার সহ জেনেরিক প্রকার (উদাহরণস্বরূপ, SoftReference<Integer> , WeakReference<MyClass> )। SoftReference , WeakReference , এবং PhantomReference ক্লাসগুলি রেফারেন্স ক্লাস থেকে উত্তরাধিকার সূত্রে প্রাপ্ত । এই ক্লাসগুলির সাথে কাজ করার সময় সবচেয়ে গুরুত্বপূর্ণ পদ্ধতিগুলি হল:
  • get() — রেফারেন্সকৃত অবজেক্ট রিটার্ন করে;

  • clear() — বস্তুর রেফারেন্স সরিয়ে দেয়।

আপনি SoftReference এবং WeakReference- এর পাঠ থেকে এই পদ্ধতিগুলি মনে রাখবেন । এটা মনে রাখা গুরুত্বপূর্ণ যে তারা বিভিন্ন ধরনের রেফারেন্সের সাথে ভিন্নভাবে কাজ করে। আজ আমরা প্রথম তিন প্রকারের মধ্যে ডুব দেব না। পরিবর্তে, আমরা ফ্যান্টম রেফারেন্স সম্পর্কে কথা বলব। আমরা অন্যান্য ধরণের রেফারেন্সগুলিতে স্পর্শ করব, তবে কেবলমাত্র তারা ফ্যান্টম রেফারেন্স থেকে কীভাবে আলাদা তা বিবেচনা করে। চলো যাই! :) শুরু করার জন্য, কেন আমাদের ফ্যান্টম রেফারেন্সের দরকার নেই? আপনি জানেন, আবর্জনা সংগ্রাহক (gc) অপ্রয়োজনীয় জাভা বস্তু দ্বারা ব্যবহৃত মেমরি প্রকাশ করে। সংগ্রাহক দুটি "পাস" এ একটি বস্তু মুছে ফেলে। প্রথম পাসে, এটি শুধুমাত্র বস্তুর দিকে তাকায় এবং প্রয়োজনে সেগুলিকে "অপ্রয়োজনীয়" (অর্থাৎ "মুছে ফেলা") হিসেবে চিহ্নিত করে। যদিfinalize()পদ্ধতি অবজেক্টের জন্য ওভাররাইড করা হয়েছে, এটি বলা হয়। অথবা হয়তো এটি বলা হয় না - এটি শুধুমাত্র আপনি ভাগ্যবান কিনা তা নির্ভর করে। আপনি সম্ভবত মনে রাখবেন যে finalize()চঞ্চল :) আবর্জনা সংগ্রহকারীর দ্বিতীয় পাসে, বস্তুটি মুছে ফেলা হয় এবং মেমরি মুক্ত হয়। আবর্জনা সংগ্রহকারীর অপ্রত্যাশিত আচরণ আমাদের জন্য অনেক সমস্যার সৃষ্টি করে। আমরা ঠিক জানি না কখন আবর্জনা সংগ্রহকারী কাজ শুরু করবে। finalize()পদ্ধতি বলা হবে কিনা আমরা জানি না । প্লাস, একটি বস্তুর একটি শক্তিশালী রেফারেন্স তৈরি করা যেতে পারে যখন এটির finalize()পদ্ধতিটি কার্যকর করা হচ্ছে, এই ক্ষেত্রে বস্তুটি মুছে ফেলা হবে না। উপলব্ধ মেমরির উপর ভারী চাহিদা তৈরি করে এমন প্রোগ্রামগুলির জন্য, এটি সহজেই একটি OutOfMemoryError. এই সব আমাদের ধাক্কা দেয় ফ্যান্টম রেফারেন্স ব্যবহার করতে. আসল বিষয়টি হ'ল এটি আবর্জনা সংগ্রহকারীর আচরণকে পরিবর্তন করে। যদি বস্তুর শুধুমাত্র ফ্যান্টম রেফারেন্স থাকে, তাহলে:
  • এর চূড়ান্তকরণ () পদ্ধতি বলা হয় (যদি এটি ওভাররাইড করা হয়)

  • ফাইনালাইজ() পদ্ধতি শেষ হওয়ার পরে যদি কিছুই পরিবর্তন না হয় এবং অবজেক্টটি এখনও মুছে ফেলা যায়, তাহলে অবজেক্টের ফ্যান্টম রেফারেন্স একটি বিশেষ সারিতে রাখা হয়: 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("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() মেথড দরকার , যা আমরা একে আলাদা মেইন ক্লাসে রাখব । সেই পদ্ধতিতে, আমরা একটি 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! 
আমরা এখানে কি দেখতে পাচ্ছি? আমাদের পরিকল্পনা মতোই সবকিছু ঘটেছে! আমাদের অবজেক্টের ফাইনালাইজ() পদ্ধতিটি ওভাররাইড করা হয়েছে এবং আবর্জনা সংগ্রাহক চলাকালীন এটিকে কল করা হয়েছিল। এর পরে, ফ্যান্টম রেফারেন্সটি রেফারেন্স কিউতে রাখা হয়েছিল । সেখানে থাকাকালীন, এর ক্লিয়ার() পদ্ধতিকে কল করা হয়েছিল (যার মধ্যে আমরা কনসোলে আউটপুট দেওয়ার জন্য ক্লিনআপ() বলেছি)। অবশেষে, বস্তুটি মেমরি থেকে মুছে ফেলা হয়েছে। এখন আপনি দেখতে পাচ্ছেন যে এটি কীভাবে কাজ করে :) অবশ্যই, আপনাকে ফ্যান্টম রেফারেন্স সম্পর্কে সমস্ত তত্ত্ব মুখস্ত করতে হবে না। তবে অন্তত মূল বিষয়গুলো মনে রাখলে ভালো হবে। প্রথমত, এগুলি হল সবথেকে দুর্বল রেফারেন্স। এগুলি তখনই কার্যকর হয় যখন বস্তুর অন্য কোন উল্লেখ অবশিষ্ট থাকে না। জাভাতে ফ্যান্টম রেফারেন্স - 1আমরা উপরে যে রেফারেন্সগুলি দিয়েছি তা সবথেকে শক্তিশালী থেকে দুর্বলতম পর্যন্ত নিচের ক্রমে সাজানো হয়েছে: 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