नमस्ते! आज की चर्चा में, हम जावा में "फैंटम रेफरेंस" (फैंटम रेफरेंस) के बारे में विस्तार से बात करेंगे। ये किस प्रकार के संदर्भ हैं? उन्हें "प्रेत संदर्भ" क्यों कहा जाता है? इनका उपयोग कैसे किया जाता है? जैसा कि आपको याद होगा, जावा में 4 प्रकार के संदर्भ होते हैं:
ऊपर दिए गए संदर्भों की सूची को सबसे मजबूत से सबसे कमजोर क्रम में अवरोही क्रम में क्रमबद्ध किया गया है: स्ट्रांग रेफरेंस -> सॉफ्ट रेफरेंस -> वीक रेफरेंस -> फैंटम रेफरेंस एक फैंटम रेफरेंस लड़ाई में तभी प्रवेश करता है जब हमारे ऑब्जेक्ट के लिए कोई मजबूत, सॉफ्ट या कमजोर संदर्भ नहीं होते हैं: ) दूसरा, प्रेत संदर्भ के लिए प्राप्त () विधि हमेशा शून्य होती है। यहां एक सरल उदाहरण दिया गया है जहां हम तीन अलग-अलग प्रकार की कारों के लिए तीन अलग-अलग प्रकार के संदर्भ बनाते हैं:
-
स्ट्रांग रेफरेंस (साधारण संदर्भ जो हम ऑब्जेक्ट बनाते समय बनाते हैं):
Cat cat = new Cat()
इस उदाहरण में, बिल्ली एक मजबूत संदर्भ है।
-
सॉफ्ट रेफरेंस (सॉफ्ट रेफरेंस)। ऐसे संदर्भों के बारे में हमारे पास एक सबक था।
-
कमजोर संदर्भ (कमजोर संदर्भ)। यहां उनके बारे में एक सीख भी दी गई थी ।
-
प्रेत संदर्भ (प्रेत संदर्भ)।
-
get () - संदर्भित वस्तु लौटाता है;
- स्पष्ट () - वस्तु के संदर्भ को हटा देता है।
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 से विरासत में मिले । हमें ऐसी कक्षा की आवश्यकता क्यों है? यह सब सीधा है। यह हमें यह सत्यापित करने के लिए स्पष्ट () विधि में अतिरिक्त तर्क जोड़ने की अनुमति देगा कि प्रेत संदर्भ वास्तव में साफ़ हो गया है (जिसका अर्थ है कि वस्तु हटा दी गई है)।
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 क्लास में रखेंगे । उस पद्धति में, हम एक टेस्ट क्लास ऑब्जेक्ट, इसके लिए एक प्रेत संदर्भ, और प्रेत संदर्भों के लिए एक कतार तैयार करेंगे। उसके बाद, हम कूड़ा बीनने वाले को बुलाएंगे और देखेंगे कि क्या होता है :)
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!
हम यहाँ क्या देखते हैं? सब कुछ वैसा ही हुआ जैसा हमने प्लान किया था! हमारी वस्तु का अंतिम रूप () विधि ओवरराइड है और इसे कचरा संग्राहक के चलने के दौरान बुलाया गया था। अगला, प्रेत संदर्भ को ReferenceQueue में रखा गया था । जबकि वहाँ, इसकी स्पष्ट () विधि को कॉल किया गया था (जिसके भीतर हमने कंसोल को आउटपुट करने के लिए क्लीनअप () कहा था)। अंत में, वस्तु को स्मृति से हटा दिया गया। अब आप देख सकते हैं कि यह कैसे काम करता है :) बेशक, आपको प्रेत संदर्भों के बारे में सभी सिद्धांतों को याद रखने की आवश्यकता नहीं है। लेकिन यह अच्छा होगा कि आप कम से कम मुख्य बातों को याद रखें। सबसे पहले, ये सभी का सबसे कमजोर संदर्भ हैं। वे तभी काम में आते हैं जब वस्तु के लिए कोई अन्य संदर्भ नहीं बचा हो। 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
प्राप्त () विधि नरम और कमजोर संदर्भों के लिए पूरी तरह से सामान्य वस्तुओं को लौटाती है, लेकिन यह प्रेत संदर्भों के लिए शून्य हो जाती है। तीसरा, प्रेत संदर्भ मुख्य रूप से स्मृति से वस्तुओं को हटाने के लिए जटिल प्रक्रियाओं में उपयोग किए जाते हैं। इतना ही! :) यह आज के हमारे पाठ का समापन करता है। लेकिन आप अकेले सिद्धांत पर बहुत दूर नहीं जा सकते हैं, इसलिए यह समय हल करने वाले कार्यों पर लौटने का है! :)
GO TO FULL VERSION