अंतिम रूप देनेवाला

From alpha
Jump to navigation Jump to search

कंप्यूटर विज्ञान में, अंतिमीकरण या अंतिमीकरण विधि एक विशेष विधि (कंप्यूटर प्रोग्रामिंग) है जो अंतिम रूप देती है, आम तौर पर कुछ प्रकार की सफाई करती है। एक फाइनलाइज़र को ऑब्जेक्ट विनाश के दौरान निष्पादित किया जाता है, वस्तु निर्माण आवंटन रद्द से पहले, और एक प्रारंभकर्ता का पूरक होता है, जिसे मेमोरी प्रबंधन के बाद ऑब्जेक्ट निर्माण के दौरान निष्पादित किया जाता है। उचित उपयोग में कठिनाई और उनके द्वारा जोड़ी गई जटिलता के कारण कुछ लोगों द्वारा फ़ाइनलाइज़र को दृढ़ता से हतोत्साहित किया जाता है, और इसके बजाय विकल्प सुझाए जाते हैं, मुख्य रूप से निपटान पैटर्न[1] (देखें #समस्याएँ)।

फ़ाइनलाइज़र शब्द का उपयोग अधिकतर ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग |ऑब्जेक्ट-ओरिएंटेड और कार्यात्मक प्रोग्रामिंग प्रोग्रामिंग भाषाओं में किया जाता है जो कचरा संग्रह (कंप्यूटर विज्ञान) का उपयोग करते हैं, जिसका मूलरूप स्मॉलटॉक है। इसकी तुलना एक विध्वंसक (कंप्यूटर प्रोग्रामिंग) से की जाती है, जो नियतात्मक वस्तु जीवनकाल वाली भाषाओं में अंतिम रूप देने के लिए कहा जाने वाला एक तरीका है, जो आदर्श रूप से C++ है।[2][3] ये आम तौर पर विशिष्ट होते हैं: एक भाषा में या तो फ़ाइनलाइज़र होंगे (यदि स्वचालित रूप से कचरा एकत्र किया जाता है) या विध्वंसक (यदि मैन्युअल रूप से मेमोरी प्रबंधित की जाती है), लेकिन दुर्लभ मामलों में एक भाषा में दोनों हो सकते हैं, जैसे कि C++/CLI और D (प्रोग्रामिंग भाषा), और संदर्भ गिनती के मामले में (कचरा संग्रहण का पता लगाने के बजाय), शब्दावली भिन्न होती है। तकनीकी उपयोग में, फाइनलाइज़र का उपयोग विनाशकों को संदर्भित करने के लिए भी किया जा सकता है, क्योंकि ये अंतिमकरण भी करते हैं, और कुछ सूक्ष्म अंतर निकाले जाते हैं - #शब्दावली देखें। फ़ाइनल शब्द का उपयोग उस वर्ग को इंगित करने के लिए भी किया जाता है जो वंशानुक्रम (वस्तु-उन्मुख प्रोग्रामिंग) नहीं हो सकता है; यह असंबंधित है.

शब्दावली

अंतिमीकरण और समापन बनाम विध्वंसक और विनाश की शब्दावली लेखकों के बीच भिन्न होती है और कभी-कभी अस्पष्ट होती है।

सामान्य उपयोग में, विध्वंसक एक ऐसी विधि है जिसे वस्तु विनाश पर नियतिवादी कहा जाता है, और मूलरूप C++ विध्वंसक है; जबकि फाइनलाइज़र को कचरा संग्रहकर्ता द्वारा गैर-नियतात्मक रूप से कहा जाता है, और मूलरूप जावा (प्रोग्रामिंग भाषा) है finalize तरीके.

उन भाषाओं के लिए जो संदर्भ गिनती के माध्यम से कचरा संग्रह को लागू करते हैं, शब्दावली अलग-अलग होती है, कुछ भाषाएं जैसे उद्देश्य सी और पर्ल डिस्ट्रक्टर का उपयोग करते हैं, और अन्य भाषाएं जैसे पायथन फाइनलाइज़र का उपयोग करते हैं (प्रति स्पेक, पायथन कचरा एकत्र किया जाता है, लेकिन संदर्भ सीपीथॉन कार्यान्वयन इसके बाद से होता है) संस्करण 2.0 संदर्भ गणना और कचरा संग्रहण के संयोजन का उपयोग करता है)। यह दर्शाता है कि संदर्भ गणना का परिणाम अर्ध-नियतात्मक वस्तु जीवनकाल में होता है: उन वस्तुओं के लिए जो चक्र का हिस्सा नहीं हैं, जब संदर्भ गिनती शून्य हो जाती है तो वस्तुएं नियतात्मक रूप से नष्ट हो जाती हैं, लेकिन जो वस्तुएं चक्र का हिस्सा होती हैं वे गैर-नियतात्मक रूप से नष्ट हो जाती हैं, जैसे कचरा संग्रहण के एक अलग रूप का हिस्सा।

कुछ संकीर्ण तकनीकी उपयोग में, कंस्ट्रक्टर और डिस्ट्रक्टर भाषा-स्तरीय शब्द हैं, जिसका अर्थ है एक वर्ग में परिभाषित विधियाँ, जबकि इनिशियलाइज़र और फ़ाइनलाइज़र कार्यान्वयन-स्तर के शब्द हैं, जिसका अर्थ है ऑब्जेक्ट निर्माण या विनाश के दौरान बुलाए गए तरीके। इस प्रकार, उदाहरण के लिए सी शार्प (प्रोग्रामिंग भाषा) के लिए मूल विनिर्देश | सी # भाषा को विनाशकों के रूप में संदर्भित किया जाता है, भले ही सी # कचरा-एकत्रित है, लेकिन सामान्य भाषा इन्फ्रास्ट्रक्चर (सीएलआई) के लिए विनिर्देश, और इसके रनटाइम वातावरण के कार्यान्वयन के रूप में सामान्य भाषा रनटाइम (सीएलआर), जिसे फाइनलाइज़र कहा जाता है। यह C# भाषा समिति के नोट्स में परिलक्षित होता है, जो आंशिक रूप से पढ़ता है: C# कंपाइलर डिस्ट्रक्टर्स को ... [संभवतः] इंस्टेंस फाइनलाइज़र [s] में संकलित करता है।[4][5] यह शब्दावली भ्रमित करने वाली है, और इस प्रकार C# विनिर्देश के नवीनतम संस्करण भाषा-स्तरीय विधि को फ़ाइनलाइज़र के रूप में संदर्भित करते हैं।[6]

एक अन्य भाषा जो इस शब्दावली में अंतर नहीं करती, वह है डी। हालांकि डी कक्षाएं कचरा एकत्र करती हैं, उनके सफाई कार्यों को विध्वंसक कहा जाता है।[7]


उपयोग

फ़ाइनलाइज़ेशन का उपयोग ज़्यादातर सफ़ाई के लिए, मेमोरी या अन्य संसाधनों को रिलीज़ करने के लिए किया जाता है: मैन्युअल मेमोरी प्रबंधन के माध्यम से आवंटित मेमोरी को हटाने के लिए; यदि संदर्भ गणना का उपयोग किया जाता है तो संदर्भों को साफ़ करने के लिए (संदर्भ गणना में कमी); संसाधनों को जारी करना, विशेष रूप से संसाधन अधिग्रहण आरंभीकरण (आरएआईआई) मुहावरे में; या किसी वस्तु का पंजीकरण रद्द करना। अंतिमीकरण की मात्रा भाषाओं के बीच महत्वपूर्ण रूप से भिन्न होती है, C++ में व्यापक अंतिमीकरण से, जिसमें मैन्युअल मेमोरी प्रबंधन, संदर्भ गणना और नियतात्मक वस्तु जीवनकाल होता है; जावा में अक्सर कोई अंतिम रूप नहीं दिया जाता है, जिसमें गैर-नियतात्मक वस्तु जीवनकाल होता है और इसे अक्सर ट्रेसिंग कचरा संग्रहकर्ता के साथ कार्यान्वित किया जाता है। यह भी संभव है कि बहुत कम या कोई स्पष्ट (उपयोगकर्ता-निर्दिष्ट) अंतिमीकरण न हो, लेकिन महत्वपूर्ण अंतर्निहित अंतिमीकरण, संकलक, दुभाषिया या रनटाइम द्वारा किया जाए; यह स्वचालित संदर्भ गिनती के मामले में आम है, जैसे कि पायथन के सीपीथॉन संदर्भ कार्यान्वयन में, या ऐप्पल के ऑब्जेक्टिव-सी के कार्यान्वयन में स्वचालित संदर्भ गिनती में, जो दोनों स्वचालित रूप से अंतिम रूप देने के दौरान संदर्भों को तोड़ देते हैं। एक अंतिमकर्ता में मनमाना कोड शामिल हो सकता है; एक विशेष रूप से जटिल उपयोग ऑब्जेक्ट को ऑब्जेक्ट पूल में स्वचालित रूप से वापस करना है।

अंतिम रूप देने के दौरान मेमोरी डीलोकेशन C++ जैसी भाषाओं में आम है जहां मैन्युअल मेमोरी प्रबंधन मानक है, लेकिन प्रबंधित भाषाओं में भी ऐसा होता है जब मेमोरी को प्रबंधित ढेर के बाहर (भाषा के बाहर) आवंटित किया गया है; जावा में यह जावा नेटिव इंटरफ़ेस (जेएनआई) और के साथ होता है ByteBuffer नॉन-ब्लॉकिंग I/O (जावा)|न्यू I/O (NIO) में ऑब्जेक्ट। कचरा संग्रहकर्ता इन बाहरी संसाधनों को ट्रैक करने में असमर्थ होने के कारण यह समस्याएँ पैदा कर सकता है, इसलिए उन्हें पर्याप्त रूप से एकत्र नहीं किया जाएगा, और अप्रबंधित मेमोरी के ख़त्म होने के कारण आउट-ऑफ़-मेमोरी त्रुटियों का कारण बन सकता है - इसे मूल मेमोरी का इलाज करके टाला जा सकता है एक संसाधन के रूप में और निपटान पैटर्न का उपयोग करते हुए, जैसा कि नीचे चर्चा की गई है।

फ़ाइनलाइज़र आम तौर पर विध्वंसकों की तुलना में बहुत कम आवश्यक और बहुत कम उपयोग किए जाते हैं। वे बहुत कम आवश्यक हैं क्योंकि कचरा संग्रहण स्मृति प्रबंधन को स्वचालित करता है, और बहुत कम उपयोग किया जाता है क्योंकि उन्हें आम तौर पर नियतात्मक रूप से निष्पादित नहीं किया जाता है - उन्हें समय पर या बिल्कुल भी नहीं बुलाया जा सकता है, और निष्पादन वातावरण की भविष्यवाणी नहीं की जा सकती है - और इस प्रकार कोई भी सफ़ाई जो एक नियतिवादी तरीके से की जानी चाहिए, उसे किसी अन्य विधि से किया जाना चाहिए, अक्सर डिस्पोज़ पैटर्न के माध्यम से मैन्युअल रूप से किया जाना चाहिए। विशेष रूप से, जावा और पायथन दोनों इस बात की गारंटी नहीं देते हैं कि फाइनलाइज़र को कभी भी बुलाया जाएगा, और इस प्रकार सफाई के लिए उन पर भरोसा नहीं किया जा सकता है।

उनके निष्पादन पर प्रोग्रामर के नियंत्रण की कमी के कारण, आमतौर पर किसी भी लेकिन सबसे तुच्छ संचालन के लिए अंतिम रूप देने से बचने की सिफारिश की जाती है। विशेष रूप से, विध्वंसकों में किए जाने वाले ऑपरेशन आमतौर पर अंतिमकर्ताओं के लिए उपयुक्त नहीं होते हैं। एक सामान्य विरोधी पैटर्न यह है कि अंतिमकर्ताओं को ऐसे लिखा जाए जैसे कि वे विध्वंसक हों, जो कि अंतिमकर्ताओं और विध्वंसकों के बीच अंतर के कारण अनावश्यक और अप्रभावी दोनों है। यह विशेष रूप से C++ प्रोग्रामर के बीच आम है, क्योंकि रिसोर्स एक्विजिशन इज़ इनिशियलाइज़ेशन (RAII) मुहावरे का पालन करते हुए मुहावरेदार C++ में डिस्ट्रक्टर्स का भारी उपयोग किया जाता है।

वाक्यविन्यास

फ़ाइनलाइज़र का उपयोग करने वाली प्रोग्रामिंग भाषाओं में C++/CLI, C शार्प (प्रोग्रामिंग भाषा)|C#, क्लीन (प्रोग्रामिंग भाषा), गो (प्रोग्रामिंग भाषा), जावा (प्रोग्रामिंग भाषा), जावास्क्रिप्ट (प्रोग्रामिंग भाषा) और पायथन (प्रोग्रामिंग भाषा) शामिल हैं। वाक्य-विन्यास भाषा के अनुसार काफी भिन्न होता है।

जावा में, फ़ाइनलाइज़र एक विधि है जिसे कहा जाता है finalize, जो ओवरराइड करता है Object.finalize तरीका।[8]

जावास्क्रिप्ट में, फाइनलाइज़ेशनरजिस्ट्री आपको किसी ऑब्जेक्ट के कचरा एकत्र होने पर कॉलबैक का अनुरोध करने की अनुमति देता है।

पायथन में, फ़ाइनलाइज़र एक विधि है जिसे कहा जाता है __del__.

पर्ल में, फ़ाइनलाइज़र एक विधि है जिसे कहा जाता है DESTROY.

C# में UML क्लास जिसमें एक कंस्ट्रक्टर और एक फ़ाइनलाइज़र होता है।

C# में, एक फाइनलाइज़र (मानक के पुराने संस्करणों में डिस्ट्रक्टर कहा जाता है) एक विधि है जिसका नाम क्लास का नाम है ~ उपसर्ग, जैसे कि ~Foo - यह C++ डिस्ट्रक्टर के समान सिंटैक्स है, और अलग-अलग व्यवहार होने के बावजूद, C++ के अनुरूप इन विधियों को मूल रूप से डिस्ट्रक्टर्स कहा जाता था, लेकिन इसके कारण उत्पन्न भ्रम के कारण इनका नाम बदलकर फाइनलाइज़र कर दिया गया।[6]

C++/CLI में, जिसमें डिस्ट्रक्टर्स और फाइनलाइज़र दोनों होते हैं, डिस्ट्रक्टर एक ऐसी विधि है जिसका नाम क्लास का नाम है ~ उपसर्ग, जैसे कि ~Foo (जैसा कि C# में है), और फ़ाइनलाइज़र एक विधि है जिसका नाम क्लास का नाम है ! उपसर्ग, जैसे कि !Foo.

गो में फ़ाइनलाइज़र को कॉल करके एकल पॉइंटर पर लागू किया जाता है runtime.SetFinalizer मानक पुस्तकालय में कार्य करें।[9]


कार्यान्वयन

फ़ाइनलाइज़र को तब कहा जाता है जब किसी ऑब्जेक्ट (कंप्यूटर विज्ञान) में कचरा एकत्र किया जाता है - किसी ऑब्जेक्ट के कचरा (पहुँच से बाहर) हो जाने के बाद, लेकिन उसकी मेमोरी को हटाए जाने से पहले। अंतिम रूप देना कचरा संग्रहकर्ता के विवेक पर गैर-नियतात्मक रूप से होता है, और कभी भी नहीं हो सकता है। यह विध्वंसकों के विपरीत है, जिन्हें किसी वस्तु के उपयोग में नहीं रहने पर नियतिवादी रूप से बुलाया जाता है, और अनियंत्रित प्रोग्राम समाप्ति के मामले को छोड़कर, हमेशा बुलाया जाता है। ऑब्जेक्ट-विशिष्ट संचालन करने की आवश्यकता के कारण, फ़ाइनलाइज़र अक्सर उदाहरण विधियां होती हैं।

कचरा संग्रहकर्ता को वस्तु के पुनरुत्थान की संभावना का भी ध्यान रखना चाहिए। आमतौर पर यह पहले फाइनलाइजर्स को क्रियान्वित करके किया जाता है, फिर जाँच की जाती है कि क्या कोई वस्तु पुनर्जीवित हो गई है, और यदि हां, तो उनके विनाश को रोक दिया जाता है। यह अतिरिक्त जांच संभावित रूप से महंगी है - एक सरल कार्यान्वयन सभी कचरे की दोबारा जांच करता है यदि एक भी ऑब्जेक्ट में फाइनलाइज़र है - और इस प्रकार दोनों धीमा हो जाते हैं और कचरा संग्रहण को जटिल बनाते हैं। इस कारण से, फ़ाइनलाइज़र वाली वस्तुओं को बिना फ़ाइनलाइज़र वाली वस्तुओं की तुलना में कम बार एकत्र किया जा सकता है (केवल कुछ चक्रों पर), जिससे त्वरित अंतिमकरण पर भरोसा करने से होने वाली समस्याएं बढ़ जाती हैं, जैसे कि संसाधन लीक।

यदि किसी वस्तु को पुनर्जीवित किया जाता है, तो अगला सवाल यह है कि क्या इसके अंतिमकर्ता को फिर से बुलाया जाता है, जब यह अगली बार नष्ट हो जाता है - विनाशकों के विपरीत, अंतिमकरणकर्ताओं को संभावित रूप से कई बार बुलाया जाता है। यदि पुनर्जीवित वस्तुओं को अंतिम रूप देने वालों को बुलाया जाता है, तो वस्तुएं बार-बार खुद को पुनर्जीवित कर सकती हैं और अविनाशी हो सकती हैं; यह Python 3.4 से पहले Python के CPython कार्यान्वयन और C# जैसी CLR भाषाओं में होता है। इससे बचने के लिए, जावा, ऑब्जेक्टिव-सी (कम से कम हाल के ऐप्पल कार्यान्वयन में), और पायथन 3.4 से पायथन सहित कई भाषाओं में, ऑब्जेक्ट को अधिकतम एक बार अंतिम रूप दिया जाता है, जिसके लिए ऑब्जेक्ट को अभी तक अंतिम रूप दिए जाने पर ट्रैकिंग की आवश्यकता होती है।

अन्य मामलों में, विशेष रूप से सी# जैसी सीएलआर भाषाओं में, अंतिमीकरण को वस्तुओं से अलग से ट्रैक किया जाता है, और वस्तुओं को अंतिम रूप देने के लिए बार-बार पंजीकृत या अपंजीकृत किया जा सकता है।

समस्याएँ

कार्यान्वयन के आधार पर, अंतिम रूप देने वाले महत्वपूर्ण संख्या में समस्याएं पैदा कर सकते हैं, और इस प्रकार कई अधिकारियों द्वारा दृढ़ता से हतोत्साहित किया जाता है।[10][11] इन समस्याओं में शामिल हैं:[10]* अंतिम रूप देने वालों को समय पर या बिल्कुल भी नहीं बुलाया जा सकता है, इसलिए स्थिति पर बने रहने, दुर्लभ संसाधनों को जारी करने, या कोई अन्य महत्वपूर्ण कार्य करने के लिए उन पर भरोसा नहीं किया जा सकता है।

  • फ़ाइनलाइज़र के परिणामस्वरूप वस्तु पुनरुत्थान हो सकता है, जो अक्सर एक प्रोग्रामिंग त्रुटि होती है और जिसकी संभावना काफी हद तक धीमी हो जाती है और कचरा संग्रहण को जटिल बना देती है।
  • फ़ाइनलाइज़र कचरा संग्रहण के आधार पर चलाए जाते हैं, जो आम तौर पर प्रबंधित मेमोरी दबाव पर आधारित होता है - वे अन्य संसाधनों की कमी के मामले में नहीं चलाए जाते हैं, और इस प्रकार अन्य दुर्लभ संसाधनों के प्रबंधन के लिए उपयुक्त नहीं होते हैं।
  • फ़ाइनलाइज़र एक निर्दिष्ट क्रम में नहीं चलते हैं, और वर्ग अपरिवर्तनीय पर भरोसा नहीं कर सकते हैं (क्योंकि वे अन्य वस्तुओं को संदर्भित कर सकते हैं जिन्हें पहले ही अंतिम रूप दिया जा चुका है)।
  • धीमे फाइनलाइजर्स अन्य फाइनलाइजर्स में देरी कर सकते हैं।
  • फ़ाइनलाइज़र के भीतर अपवादों को आम तौर पर नियंत्रित नहीं किया जा सकता है, क्योंकि फ़ाइनलाइज़र एक अनिर्दिष्ट वातावरण में चलाया जाता है, और इसे या तो अनदेखा किया जा सकता है या अनियंत्रित प्रोग्राम समाप्ति का कारण बन सकता है।
  • फ़ाइनलाइज़र प्रोग्राम इनवेरिएंट का उल्लंघन करते हुए, लाइव ऑब्जेक्ट को संदर्भित और गलती से अंतिम रूप दे सकते हैं।
  • फ़ाइनलाइज़र सिंक्रनाइज़ेशन समस्याओं का कारण बन सकते हैं, यहां तक ​​कि अन्यथा अनुक्रमिक (एकल-थ्रेडेड) प्रोग्राम में भी, जब फ़ाइनलाइज़ेशन एक अलग थ्रेड में किया जाता है।[12]
  • यदि लॉक जैसे सिंक्रनाइज़ेशन तंत्र का उपयोग किया जाता है, तो फ़ाइनलाइज़र गतिरोध का कारण बन सकता है, एक निर्दिष्ट क्रम में नहीं चलने और संभवतः समवर्ती रूप से चलने के कारण।
  • प्रोग्राम समाप्ति के दौरान चलाए जाने वाले फ़ाइनलाइज़र सामान्य रनटाइम वातावरण पर भरोसा नहीं कर सकते हैं, और इस प्रकार गलत धारणाओं के कारण विफल हो सकते हैं - इस कारण से फ़ाइनलाइज़र अक्सर समाप्ति के दौरान नहीं चलाए जाते हैं।

इसके अलावा, प्रोग्रामिंग त्रुटियों के कारण या अप्रत्याशित रीचैबिलिटी के कारण, कचरा होने की उम्मीद से परे वस्तुओं के पहुंच योग्य रहने के कारण फाइनलाइज़र चलने में विफल हो सकते हैं। उदाहरण के लिए, जब पायथन एक अपवाद पकड़ता है (या एक अपवाद इंटरैक्टिव मोड में नहीं पकड़ा जाता है), तो यह स्टैक फ्रेम का एक संदर्भ रखता है जहां अपवाद उठाया गया था, जो उस स्टैक फ्रेम से संदर्भित वस्तुओं को जीवित रखता है।

जावा में, सुपरक्लास में फ़ाइनलाइज़र भी उपवर्ग में कचरा संग्रहण को धीमा कर सकते हैं, क्योंकि फ़ाइनलाइज़र संभावित रूप से उपवर्ग में फ़ील्ड को संदर्भित कर सकता है, और इस प्रकार फ़ाइनलाइज़र चलने के बाद, फ़ील्ड को अगले चक्र तक कचरा एकत्र नहीं किया जा सकता है।[10]वंशानुक्रम पर रचना का उपयोग करके इससे बचा जा सकता है।

संसाधन प्रबंधन

C++ के रिसोर्स एक्विजिशन इज़ इनिशियलाइज़ेशन (RAII) मुहावरे के अनुरूप, संसाधनों को रिलीज़ करने के लिए फ़ाइनलाइज़र का उपयोग करना एक सामान्य एंटी-पैटर्न है: इनिशियलाइज़र (कन्स्ट्रक्टर) में एक संसाधन प्राप्त करें, और इसे फ़ाइनलाइज़र (डिस्ट्रक्टर) में रिलीज़ करें। कई कारणों से यह काम नहीं करता. मूल रूप से, फ़ाइनलाइज़र को कभी भी नहीं बुलाया जा सकता है, और अगर बुलाया भी जाता है, तो उसे समय पर नहीं बुलाया जा सकता है - इस प्रकार संसाधनों को जारी करने के लिए फ़ाइनलाइज़र का उपयोग आम तौर पर संसाधन लीक का कारण बनेगा। इसके अलावा, अंतिम रूप देने वालों को एक निर्धारित क्रम में नहीं बुलाया जाता है, जबकि संसाधनों को अक्सर एक विशिष्ट क्रम में जारी करने की आवश्यकता होती है, अक्सर विपरीत क्रम जिसमें उन्हें हासिल किया गया था। साथ ही, चूंकि अंतिमकर्ताओं को कचरा संग्रहकर्ता के विवेक पर बुलाया जाता है, उन्हें अक्सर केवल प्रबंधित मेमोरी दबाव के तहत ही बुलाया जाएगा (जब थोड़ी प्रबंधित मेमोरी उपलब्ध हो), संसाधन दबाव की परवाह किए बिना - यदि दुर्लभ संसाधन कचरे के पास हैं लेकिन प्रचुर मात्रा में हैं प्रबंधित मेमोरी उपलब्ध होने पर, कचरा संग्रहण नहीं हो सकता है, इस प्रकार इन संसाधनों को पुनः प्राप्त नहीं किया जा सकता है।

इस प्रकार स्वचालित संसाधन प्रबंधन के लिए फ़ाइनलाइज़र का उपयोग करने के बजाय, कचरा-एकत्रित भाषाओं में आमतौर पर निपटान पैटर्न का उपयोग करके संसाधनों को मैन्युअल रूप से प्रबंधित करना चाहिए। इस मामले में संसाधन अभी भी इनिशियलाइज़र में प्राप्त किए जा सकते हैं, जिसे ऑब्जेक्ट इंस्टेंटिएशन पर स्पष्ट रूप से कहा जाता है, लेकिन निपटान विधि में जारी किया जाता है। डिस्पोज़ विधि को स्पष्ट रूप से, या C# जैसी भाषा संरचनाओं द्वारा परोक्ष रूप से कहा जा सकता है using, मई आपको try-संसाधनों के साथ, या पायथन का with.

हालाँकि, कुछ मामलों में संसाधनों को जारी करने के लिए डिस्पोज़ पैटर्न और फ़ाइनलाइज़र दोनों का उपयोग किया जाता है। यह ज्यादातर सीएलआर भाषाओं जैसे सी # में पाया जाता है, जहां अंतिमकरण का उपयोग निपटान के लिए बैकअप के रूप में किया जाता है: जब कोई संसाधन प्राप्त किया जाता है, तो प्राप्त करने वाली वस्तु को अंतिम रूप देने के लिए कतारबद्ध किया जाता है ताकि संसाधन वस्तु विनाश पर जारी हो, भले ही संसाधन न हो मैन्युअल निपटान द्वारा जारी किया गया।

नियतात्मक और गैर-नियतात्मक वस्तु जीवनकाल

नियतात्मक वस्तु जीवनकाल वाली भाषाओं में, विशेष रूप से C++, संसाधन प्रबंधन अक्सर संसाधन कब्जे के जीवनकाल को वस्तु जीवनकाल से जोड़कर, आरंभीकरण के दौरान संसाधनों को प्राप्त करके और अंतिम रूप देने के दौरान उन्हें जारी करके किया जाता है; इसे रिसोर्स एक्विजिशन इज़ इनिशियलाइज़ेशन (RAII) के रूप में जाना जाता है। यह सुनिश्चित करता है कि संसाधन कब्ज़ा एक वर्ग अपरिवर्तनीय है, और वस्तु के नष्ट होने पर संसाधन तुरंत जारी किए जाते हैं।

हालाँकि, गैर-नियतात्मक वस्तु जीवनकाल वाली भाषाओं में - जिसमें कचरा संग्रहण वाली सभी प्रमुख भाषाएँ शामिल हैं, जैसे कि C#, जावा और पायथन - यह काम नहीं करता है, क्योंकि अंतिम रूप समय पर नहीं हो सकता है या बिल्कुल भी नहीं हो सकता है, और इस प्रकार संसाधन लंबे समय तक या बिल्कुल भी जारी नहीं किया जा सकता है, जिससे संसाधन लीक हो सकता है। इन भाषाओं में संसाधनों को आम तौर पर निपटान पैटर्न के माध्यम से मैन्युअल रूप से प्रबंधित किया जाता है: संसाधन अभी भी आरंभीकरण के दौरान प्राप्त किए जा सकते हैं, लेकिन कॉल करके जारी किए जाते हैं dispose तरीका। फिर भी, इन भाषाओं में संसाधनों को जारी करने के लिए अंतिमीकरण का उपयोग करना एक सामान्य विरोधी पैटर्न है, और कॉल करना भूल जाना है dispose फिर भी संसाधन रिसाव का कारण बनेगा.

कुछ मामलों में दोनों तकनीकों को एक स्पष्ट निपटान विधि का उपयोग करके संयोजित किया जाता है, लेकिन बैकअप के रूप में अंतिम रूप देने के दौरान किसी भी रुके हुए संसाधनों को भी जारी किया जाता है। यह आमतौर पर C# में पाया जाता है, और जब भी कोई संसाधन प्राप्त किया जाता है तो उसे अंतिम रूप देने के लिए एक ऑब्जेक्ट को पंजीकृत करके और जब भी कोई संसाधन जारी किया जाता है तो अंतिमकरण को दबाकर लागू किया जाता है।

वस्तु पुनरुत्थान

यदि उपयोगकर्ता-निर्दिष्ट अंतिमकर्ताओं को अनुमति दी जाती है, तो अंतिमकरण के लिए वस्तु पुनरुत्थान का कारण बनना संभव है, क्योंकि अंतिमकर्ता मनमाना कोड चला सकते हैं, जो जीवित वस्तुओं से नष्ट होने वाली वस्तुओं के संदर्भ बना सकते हैं। कचरा संग्रह रहित भाषाओं के लिए, यह एक गंभीर बग है, और लटकते संदर्भों और स्मृति सुरक्षा उल्लंघनों का कारण बनता है; कचरा संग्रहण वाली भाषाओं के लिए, इसे कचरा संग्रहकर्ता द्वारा रोका जाता है, आमतौर पर कचरा संग्रहण में एक और कदम जोड़कर (सभी उपयोगकर्ता-निर्दिष्ट अंतिमकर्ताओं को चलाने के बाद, पुनरुत्थान की जांच करें), जो कचरा संग्रहण को जटिल और धीमा कर देता है।

इसके अलावा, वस्तु पुनरुत्थान का अर्थ है कि किसी वस्तु को नष्ट नहीं किया जा सकता है, और पैथोलॉजिकल मामलों में कोई वस्तु हमेशा अंतिम रूप देने के दौरान खुद को पुनर्जीवित कर सकती है, जिससे वह अविनाशी हो जाती है। इसे रोकने के लिए, कुछ भाषाएँ, जैसे जावा और पायथन (पायथन 3.4 से) केवल एक बार वस्तुओं को अंतिम रूप देती हैं, और पुनर्जीवित वस्तुओं को अंतिम रूप नहीं देती हैं।[citation needed] सीधे तौर पर यह ट्रैकिंग द्वारा किया जाता है कि क्या किसी वस्तु को वस्तु-दर-वस्तु के आधार पर अंतिम रूप दिया गया है। ऑब्जेक्टिव-सी भी अंतिम रूप देने पर नज़र रखता है (कम से कम हाल में)।[when?] एप्पल संस्करण[clarification needed]) समान कारणों से, पुनरुत्थान को एक बग के रूप में मानना।

.NET फ्रेमवर्क में एक अलग दृष्टिकोण का उपयोग किया जाता है, विशेष रूप से C# और विज़ुअल बेसिक .NET, जहां अंतिमकरण को ऑब्जेक्ट के बजाय कतार द्वारा ट्रैक किया जाता है। इस मामले में, यदि उपयोगकर्ता-निर्दिष्ट फाइनलाइज़र प्रदान किया जाता है, तो डिफ़ॉल्ट रूप से ऑब्जेक्ट को केवल एक बार अंतिम रूप दिया जाता है (इसे निर्माण पर अंतिम रूप देने के लिए कतारबद्ध किया जाता है, और इसे अंतिम रूप देने के बाद हटा दिया जाता है), लेकिन इसे कॉल करके बदला जा सकता है GC मापांक। कॉल करके अंतिम रूप देने से रोका जा सकता है GC.SuppressFinalize, जो ऑब्जेक्ट को डीक्यू करता है, या कॉल करके पुनः सक्रिय करता है GC.ReRegisterForFinalize, जो वस्तु को कतारबद्ध करता है। इनका उपयोग विशेष रूप से निपटान पैटर्न के पूरक के रूप में संसाधन प्रबंधन के लिए अंतिमकरण का उपयोग करते समय, या ऑब्जेक्ट पूल को लागू करते समय किया जाता है।

प्रारंभीकरण के साथ तुलना करें

अंतिमकरण औपचारिक रूप से आरंभीकरण (प्रोग्रामिंग) का पूरक है - आरंभीकरण जीवनकाल की शुरुआत में होता है, अंतिमकरण अंत में होता है - लेकिन व्यवहार में काफी भिन्न होता है। वेरिएबल और ऑब्जेक्ट दोनों को प्रारंभ किया जाता है, ज्यादातर मान निर्दिष्ट करने के लिए, लेकिन सामान्य तौर पर केवल ऑब्जेक्ट को अंतिम रूप दिया जाता है, और सामान्य तौर पर मानों को साफ़ करने की कोई आवश्यकता नहीं होती है - मेमोरी को बस ऑपरेटिंग सिस्टम द्वारा हटाया और पुनः प्राप्त किया जा सकता है।

प्रारंभिक मान निर्दिष्ट करने के अलावा, आरंभीकरण का उपयोग अधिकतर संसाधनों को प्राप्त करने या किसी ऑब्जेक्ट को किसी सेवा (जैसे आयोजन प्रबंधकर्ता ) के साथ पंजीकृत करने के लिए किया जाता है। इन क्रियाओं में सममित रिलीज़ या अपंजीकृत क्रियाएं होती हैं, और इन्हें अंतिम रूप से सममित रूप से नियंत्रित किया जा सकता है, जो RAII में किया जाता है। हालाँकि, कई भाषाओं में, विशेष रूप से कचरा संग्रहण वाली भाषाओं में, वस्तु का जीवनकाल असममित होता है: वस्तु का निर्माण कोड में कुछ स्पष्ट बिंदु पर नियतात्मक रूप से होता है, लेकिन वस्तु का विनाश गैर-नियतात्मक रूप से, कुछ अनिर्दिष्ट वातावरण में, कचरा संग्रहकर्ता के विवेक पर होता है। इस विषमता का अर्थ है कि अंतिमीकरण को आरंभीकरण के पूरक के रूप में प्रभावी ढंग से उपयोग नहीं किया जा सकता है, क्योंकि यह समयबद्ध तरीके से, निर्दिष्ट क्रम में या निर्दिष्ट वातावरण में नहीं होता है। एक स्पष्ट बिंदु पर वस्तु का निपटान करके समरूपता को आंशिक रूप से बहाल किया जाता है, लेकिन इस मामले में निपटान और विनाश एक ही बिंदु पर नहीं होता है, और एक वस्तु एक निपटान लेकिन अभी भी जीवित स्थिति में हो सकती है, जो वर्ग अपरिवर्तनीय को कमजोर करती है और उपयोग को जटिल बनाता है.

वेरिएबल्स को आम तौर पर उनके जीवनकाल की शुरुआत में आरंभ किया जाता है, लेकिन उनके जीवनकाल के अंत में अंतिम रूप नहीं दिया जाता है - हालांकि यदि किसी वेरिएबल का मान एक ऑब्जेक्ट है, तो ऑब्जेक्ट को अंतिम रूप दिया जा सकता है। कुछ मामलों में वेरिएबल्स को भी अंतिम रूप दिया जाता है: जीसीसी एक्सटेंशन वेरिएबल्स को अंतिम रूप देने की अनुमति देते हैं।

के साथ संबंध finally

जैसा कि नामकरण, अंतिम रूप देने और में परिलक्षित होता है finally दोनों निर्माण समान उद्देश्यों को पूरा करते हैं: कुछ अंतिम क्रिया करना, आम तौर पर कुछ और समाप्त होने के बाद सफाई करना। जब वे घटित होते हैं तो उनमें भिन्नता होती है - a finally क्लॉज तब निष्पादित होता है जब प्रोग्राम निष्पादन संबंधित की बॉडी को छोड़ देता है try खंड - यह स्टैक अनवाइंड के दौरान होता है, और इस प्रकार लंबित का एक स्टैक होता है finally खंड, क्रम में - जबकि अंतिम रूप तब होता है जब कोई वस्तु नष्ट हो जाती है, जो मेमोरी प्रबंधन विधि के आधार पर होती है, और सामान्य तौर पर अंतिम रूप देने की प्रतीक्षा में वस्तुओं का एक सेट होता है - अक्सर ढेर पर - जिसे किसी विशिष्ट क्रम में होने की आवश्यकता नहीं होती है।

हालाँकि, कुछ मामलों में ये मेल खाते हैं। C++ में, वस्तु विनाश नियतात्मक है, और a का व्यवहार finally क्लॉज को किसी ऑब्जेक्ट के मान के साथ एक स्थानीय वैरिएबल बनाकर तैयार किया जा सकता है, जिसका दायरा एक ब्लॉक के बॉडी से मेल खाता है try खंड - जब निष्पादन इस दायरे से बाहर निकलता है तो वस्तु को अंतिम रूप दिया जाता है (नष्ट किया जाता है), ठीक उसी तरह जैसे कि कोई था finally खंड. इस कारण से, C++ में a नहीं है finally निर्माण - अंतर यह है कि अंतिम रूप को क्लास परिभाषा में डिस्ट्रक्टर विधि के रूप में परिभाषित किया गया है, न कि कॉल साइट पर। finally खंड.

इसके विपरीत, ए के मामले में finally coroutine में क्लॉज, पायथन जनरेटर की तरह, कॉरआउटिन कभी भी समाप्त नहीं हो सकता है - केवल हमेशा उपज देता है - और इस प्रकार सामान्य निष्पादन में finally खंड कभी निष्पादित नहीं होता है. यदि कोई कोरआउटिन के उदाहरणों को वस्तुओं के रूप में व्याख्या करता है, तो finally क्लॉज को ऑब्जेक्ट को अंतिम रूप देने वाला माना जा सकता है, और इस प्रकार जब उदाहरण कचरा एकत्र किया जाता है तो इसे निष्पादित किया जा सकता है। पायथन शब्दावली में, कोरआउटिन की परिभाषा एक जेनरेटर फ़ंक्शन है, जबकि इसका एक उदाहरण जेनरेटर इटरेटर है, और इस प्रकार एक finally जेनरेटर फ़ंक्शन में क्लॉज इस फ़ंक्शन से तत्काल जेनरेट किए गए जेनरेटर इटरेटर्स में फाइनलाइज़र बन जाता है।

इतिहास

वस्तु विनाश में एक अलग चरण के रूप में अंतिम रूप देने की धारणा आज से चली आ रही है Montgomery (1994),[13] वस्तु निर्माण में आरंभीकरण के पहले भेद के अनुरूप Martin & Odell (1992).[14] इस बिंदु से पहले के साहित्य में इस प्रक्रिया के लिए विनाश का उपयोग किया गया था, अंतिमकरण और स्थानांतरण को अलग नहीं किया गया था, और इस अवधि से संबंधित प्रोग्रामिंग भाषाएं, जैसे सी ++ और पर्ल, विनाश शब्द का उपयोग करती हैं। अंतिम रूप देना और अंतिम रूप देना शब्दों का उपयोग प्रभावशाली पुस्तक डिजाइन पैटर्न्स (1994) में भी किया गया है।[lower-alpha 1][15] 1995 में जावा की शुरूआत शामिल थी finalize विधियाँ, जिन्होंने इस शब्द को लोकप्रिय बनाया और इसे कचरा संग्रहण के साथ जोड़ा, और इस बिंदु से भाषाएँ आम तौर पर यह भेद करती हैं और अंतिमकरण शब्द का उपयोग करती हैं, विशेष रूप से कचरा संग्रहण के संदर्भ में।

यह भी देखें

  • कचरा संग्रहण (कंप्यूटर विज्ञान), विशेष रूप से कचरा संग्रहण (कंप्यूटर विज्ञान) पर अनुभाग#नियतिवाद
  • वस्तु जीवनकाल
  • इनिशियलाइज़ेशन (प्रोग्रामिंग) प्रक्रिया और संबंधित इनिशियलाइज़र पैटर्न

टिप्पणियाँ

  1. Published 1994, with a 1995 copyright.


संदर्भ

  1. Jagger, Perry & Sestoft 2007, p. 542, "In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use Dispose.
  2. Boehm, Hans-J. (2002). डिस्ट्रक्टर्स, फ़ाइनलाइज़र और सिंक्रोनाइज़ेशन. Symposium on Principles of Programming Languages (POPL).
  3. Jagger, Perry & Sestoft 2007, p. 542, C++ destructors versus C# finalizers C++ destructors are determinate in the sense that they are run at known points in time, in a known order, and from a known thread. They are thus semantically very different from C# finalizers, which are run at unknown points in time, in an unknown order, from an unknown thread, and at the discretion of the garbage collector.
  4. In full: "We're going to use the term "destructor" for the member which executes when an instance is reclaimed. Classes can have destructors; structs can't. Unlike in C++, a destructor cannot be called explicitly. Destruction is non-deterministic – you can't reliably know when the destructor will execute, except to say that it executes at some point after all references to the object have been released. The destructors in an inheritance chain are called in order, from most descendant to least descendant. There is no need (and no way) for the derived class to explicitly call the base destructor. The C# compiler compiles destructors to the appropriate CLR representation. For this version that probably means an instance finalizer that is distinguished in metadata. CLR may provide static finalizers in the future; we do not see any barrier to C# using static finalizers.", May 12th, 1999.
  5. What’s the difference between a destructor and a finalizer?, Eric Lippert, Eric Lippert’s Blog: Fabulous Adventures In Coding, 21 Jan 2010
  6. 6.0 6.1 Jagger, Perry & Sestoft 2007, p. 542, "In the previous version of this standard, what is now referred to as a “finalizer” was called a “destructor”. Experience has shown that the term “destructor” caused confusion and often resulted in incorrect expectations, especially to programmers knowing C++. In C++, a destructor is called in a determinate manner, whereas, in C#, a finalizer is not. To get determinate behavior from C#, one should use Dispose."
  7. Class destructors Class destructors in D
  8. java.lang, क्लास ऑब्जेक्ट: अंतिम रूप दें
  9. "Runtime package - runtime - PKG.go.dev".
  10. 10.0 10.1 10.2 "MET12-J. Do not use finalizers", Dhruv Mohindra, The CERT Oracle Secure Coding Standard for Java, 05. Methods (MET) Archived 2014-05-04 at the Wayback Machine
  11. object.__del__(self), The Python Language Reference, 3. Data model: "... __del__() methods should do the absolute minimum needed to maintain external invariants."
  12. Hans-J. Boehm, Finalization, Threads, and the Java™ Technology-Based Memory Model, JavaOne Conference, 2005.
  13. Montgomery 1994, p. 120, "As with object instantiation, design for object termination can benefit from implementation of two operations for each class—a finalize and a terminate operation. A finalize operation breaks associations with other objects, ensuring data structure integrity."
  14. Montgomery 1994, p. 119, "Consider implementing class instantiation as a create and initialize operation, as suggested by Martin and Odell. The first allocates storage for new objects, and the second constructs the object to adhere to specifications and constraints."
  15. "Every new class has a fixed implementation overhead (initialization, finalization, etc.).", "destructor In C++, an operation that is automatically invoked to finalize an object that is about to be deleted."


अग्रिम पठन


बाहरी संबंध