2008-12-01 12 views
67

यदि मैं सही ढंग से समझता हूं तो .NET रनटाइम हमेशा मेरे बाद साफ हो जाएगा। इसलिए यदि मैं नई ऑब्जेक्ट्स बनाता हूं और मैं उन्हें अपने कोड में संदर्भित करना बंद कर देता हूं, तो रनटाइम उन ऑब्जेक्ट्स को साफ़ कर देगा और उनके द्वारा कब्जा कर लिया गया स्मृति मुक्त कर देगा।चूंकि .NET में कचरा कलेक्टर है, इसलिए हमें अंतिमकर्ता/विनाशक/निपटान-पैटर्न की आवश्यकता क्यों है?

चूंकि ऐसा कुछ मामला है तो कुछ वस्तुओं को विनाशक या निपटान विधि की आवश्यकता क्यों होती है? रनटाइम उनके बाद साफ नहीं होगा जब उन्हें संदर्भित नहीं किया जाएगा?

उत्तर

93

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

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

क्यों Dispose की जरूरत है एक त्वरित और गंदा लॉग विधि हो सकता है की एक साधारण उदाहरण:

public void Log(string line) 
{ 
    var sw = new StreamWriter(File.Open(
     "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)); 

    sw.WriteLine(line); 

    // Since we don't close the stream the FileStream finalizer will do that for 
    // us but we don't know when that will be and until then the file is locked. 
} 

उपरोक्त उदाहरण में, फ़ाइल जब तक कचरा कलेक्टर StreamWriter वस्तु पर finalizer कॉल बंद कर दिया रहेगा। यह तब से एक समस्या प्रस्तुत करता है, इस बीच, विधि को लॉग लिखने के लिए फिर से बुलाया जा सकता है, लेकिन इस बार यह विफल हो जाएगा क्योंकि फ़ाइल अभी भी लॉक है।

सही तरीका वस्तु निपटान के लिए जब यह का उपयोग किया जाता है:

public void Log(string line) 
{ 
    using (var sw = new StreamWriter(File.Open(
     "LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))) { 

     sw.WriteLine(line); 
    } 

    // Since we use the using block (which conveniently calls Dispose() for us) 
    // the file well be closed at this point. 
} 

Btw, तकनीकी रूप से finalizers और विनाशकर्ता एक ही बात मतलब है; मैं सी # विनाशकों के फाइनलाइजर्स को कॉल करना पसंद करता हूं क्योंकि अन्यथा वे सी ++ विनाशकों के साथ लोगों को भ्रमित करते हैं, जो सी # के विपरीत, निर्धारक हैं।

+4

आईएमओ यह सबसे अच्छा जवाब है। इसका सबसे महत्वपूर्ण हिस्सा - और हम डिस्पोजेबल सिंटैक्स का उपयोग क्यों करते हैं - दुर्लभ संसाधनों की * निर्धारिक रिलीज * प्रदान करना है। महान पद। – Rob

+1

अच्छा जवाब, हालांकि अंतिमकर्ता स्वचालित रूप से वस्तुओं के जीवन के अंत में नहीं चलते हैं। अन्यथा हमें डिस्पोजेबल पैटर्न की आवश्यकता नहीं होगी। उन्हें जीसी द्वारा बुलाया जाता है जब यह निर्धारित करता है कि इसे चलाने की जरूरत है (जो जानता है कि कब)। रिकॉर्ड के लिए –

+1

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

0

कुछ वस्तुओं को निम्न-स्तर की वस्तुओं को साफ करने की आवश्यकता हो सकती है। जैसे कि हार्डवेयर को बंद करने की आवश्यकता है, आदि

1

ऑब्जेक्ट्स जिन्हें अवरोधकों और निपटान विधियों की आवश्यकता है, वे गैर-प्रबंधित संसाधनों का उपयोग कर रहे हैं। तो कचरा कलेक्टर उन संसाधनों को साफ नहीं कर सकता है, और आपको इसे स्वयं करना है।

आईडीएसओएसपीबल के लिए एमएसडीएन दस्तावेज देखें; http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

उदाहरण एक गैर-प्रबंधित हैंडलर - IntPr का उपयोग करता है।

+0

जीसी संसाधनों को साफ कर सकते हैं, आप बस कब नहीं जानते। –

+2

जीसी कैन * आमतौर पर * संसाधनों को साफ करता है, लेकिन हमेशा नहीं। उदाहरण के लिए, सिस्टम.डायरेक्टरी सर्विसेज के लिए एमएसडीएन दस्तावेज में .र्चरसेलल्ट चयन: "कार्यान्वयन प्रतिबंधों के कारण, सर्चरसल्ट कोलेक्शन क्लास अपने सभी अप्रबंधित संसाधनों को तब तक जारी नहीं कर सकती जब यह कचरा इकट्ठा होता है" – Joe

0

मुख्य रूप से गैर-प्रबंधित कोड के लिए, और गैर-प्रबंधित कोड के साथ बातचीत। "शुद्ध" प्रबंधित कोड को कभी भी अंतिमकर्ता की आवश्यकता नहीं होनी चाहिए। दूसरी तरफ डिस्पोजेबल एक आसान तरीका है जब आप इसके साथ काम करते हैं तो कुछ जारी किया जा सकता है।

9

कचरा कलेक्टर केवल तभी चलाएगा जब सिस्टम मेमोरी दबाव में न हो, जब तक कि इसे वास्तव में कुछ स्मृति मुक्त करने की आवश्यकता न हो। इसका मतलब है, आप कभी भी सुनिश्चित नहीं हो सकते कि जीसी कब चलेगा।

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

आम तौर पर, अप्रत्याशित संसाधनों के साथ काम करने वाली किसी भी कक्षा पर IDISposable लागू किया जाता है।

+1

INCORRECT => "कचरा कलेक्टर केवल तभी चलाएगा जब सिस्टम स्मृति दबाव में नहीं है, जब तक कि इसे वास्तव में कुछ स्मृति मुक्त करने की आवश्यकता न हो। " दरअसल, यह कथन सत्य नहीं है। जीसी 3 मामलों के तहत चलाता है (जिसमें से केवल एक निर्धारिती है): 1) जब स्मृति आवंटन का अनुरोध किया जाता है और उस ऑब्जेक्ट पीढ़ी के लिए मौजूदा सेगमेंट आकार पार हो गया है, 2) सिस्टम मेमोरी प्रेशर (ओएस) के तहत है, 3) ऐपडोमेन को अनलोड किया जा रहा है –

+1

INCORRECT => "आम तौर पर, किसी भी वर्ग पर IDISposable लागू किया जाता है जो अप्रबंधित संसाधनों के साथ काम करता है।" यह कथन भी सच नहीं है। जब भी आप एक अप्रबंधित संसाधन –

0

.NET कचरा कलेक्टर जानता है कि .NET रनटाइम के भीतर प्रबंधित ऑब्जेक्ट्स को कैसे प्रबंधित किया जाए। लेकिन निपटान पैटर्न (IDisposable) मुख्य रूप से अन-प्रबंधित वस्तुओं के लिए उपयोग किया जाता है जो एक अनुप्रयोग का उपयोग कर रहा है।

दूसरे शब्दों में, .NET रनटाइम को यह आवश्यक नहीं है कि प्रत्येक प्रकार के डिवाइस से कैसे निपटें या वहां से बाहर निकलें (नेटवर्क कनेक्शन, फ़ाइल हैंडल, ग्राफिक्स डिवाइस इत्यादि बंद करना), इसलिए IDISposable का उपयोग करने का एक तरीका प्रदान करता है एक प्रकार में "मुझे अपने स्वयं के कुछ सफाई को लागू करने दें" कहें। कार्यान्वयन को देखते हुए, कचरा कलेक्टर निपटान() को कॉल कर सकता है और यह सुनिश्चित कर सकता है कि प्रबंधित ढेर के बाहर की चीजें साफ़ हो जाएं।

+0

से निपट रहे हैं, तो किसी भी समय सदस्य वर्ग लागू करने योग्य आईडी लागू करने योग्य पैटर्न लागू किया जाना चाहिए ... धन्यवाद "प्रबंधित ढेर" में ".NET स्टैक/हीप के बाहर" को बदलकर स्पष्ट किया गया है। –

0

कुछ (बहुत कम) मामले हैं जहां एक शुद्ध प्रबंधित ऑब्जेक्ट का उपयोग नहीं किया जाता है, जब एक शुद्ध प्रबंधित ऑब्जेक्ट का उपयोग नहीं किया जाता है, लेकिन मैं अपने सिर के ऊपर से एक उदाहरण के साथ नहीं आ सकता हूं लेकिन मैं वर्षों से कुछ वैध उपयोग देखा है। लेकिन मुख्य कारण यह है कि ऑब्जेक्ट का उपयोग करने वाले किसी भी अप्रबंधित संसाधनों को साफ करना है।

तो, सामान्य रूप से, जब तक आप अप्रबंधित संसाधनों का उपयोग नहीं कर रहे हैं, तब तक आपको निपटान/अंतिम रूप पैटर्न का उपयोग करने की आवश्यकता नहीं होगी।

4
  1. यह सफाई, आप इसे जितनी जल्दी
0

को साफ मदद कर सकते हैं कर सकते हैं चीजों कचरा कलेक्टर के बाद आप चीजों के साथ यहां तक ​​कि

  • को साफ नहीं कर सकते हैं क्योंकि कचरा कलेक्टर नहीं कर सकते प्रबंधित वातावरण को आवंटित नहीं किया गया था। इसलिए एक अप्रबंधित एपीआई के लिए कोई भी कॉल जो स्मृति आवंटन में परिणामस्वरूप पुराने तरीके से एकत्र की जानी चाहिए।

  • 2

    वास्तविक कारण यह है कि .net कचरा संग्रह अप्रबंधित संसाधन एकत्र करने के लिए डिज़ाइन नहीं किया गया है, इसलिए इन संसाधनों की सफाई अभी भी डेवलपर के हाथों में है। इसके अलावा, ऑब्जेक्ट फ़ाइनलाइज़र स्वचालित रूप से कॉल नहीं होते हैं जब ऑब्जेक्ट गुंजाइश से बाहर हो जाता है। उन्हें कुछ अनिश्चित समय पर जीसी द्वारा बुलाया जाता है। और जब उन्हें बुलाया जाता है, तो जीसी इसे तुरंत नहीं चलाता है, यह अगले दौर के लिए इसे कॉल करने के लिए इंतजार कर रहा है, और अधिक साफ करने के लिए समय बढ़ा रहा है, अच्छी बात नहीं है जब आपकी ऑब्जेक्ट्स दुर्लभ अप्रबंधित संसाधनों (जैसे फाइलें या नेटवर्क कनेक्शन)। डिस्पोजेबल पैटर्न दर्ज करें, जहां डेवलपर एक निर्धारित समय पर मैन्युअल रूप से दुर्लभ संसाधनों को जारी कर सकता है (जब yourobject.Dispose() या उपयोग (...) कथन को कॉल करते हैं)। ध्यान रखें कि आपको GC.SuppressFinalize (यह) कॉल करना चाहिए; जीसी को बताने के लिए अपने निपटान विधि में कि ऑब्जेक्ट मैन्युअल रूप से निपटाया गया था और इसे अंतिम रूप दिया जाना चाहिए। मेरा सुझाव है कि आप के। क्वेलिना और बी अब्राम द्वारा फ्रेमवर्क डिज़ाइन दिशानिर्देश पुस्तिका देखें। यह डिस्पोजेबल पैटर्न बहुत अच्छा बताता है।

    शुभकामनाएं!

    20

    पिछले उत्तर अच्छे हैं लेकिन मुझे एक बार फिर से महत्वपूर्ण बिंदु पर जोर देना है। विशेष रूप से, आपने कहा कि

    यदि मैं सही ढंग से समझता हूं कि .NET रनटाइम हमेशा मेरे बाद साफ हो जाएगा।

    यह केवल आंशिक रूप से सही है। वास्तव में, .NET केवल एक विशेष संसाधन: मुख्य स्मृति के लिए स्वचालित प्रबंधन प्रदान करता है। अन्य सभी संसाधनों को मैन्युअल सफाई की आवश्यकता है। 1)

    विचित्र रूप से, मुख्य संसाधनों को प्रोग्राम संसाधनों के बारे में लगभग सभी चर्चाओं में विशेष स्थिति मिलती है। इसके लिए निश्चित रूप से एक अच्छा कारण है - मुख्य स्मृति अक्सर सबसे कठिन संसाधन होता है। लेकिन यह याद रखना उचित है कि अन्य प्रकार के संसाधन भी हैं, जिन्हें प्रबंधन की भी आवश्यकता है। इसलिए finalizers के अस्तित्व -


    1) सामान्य करने का प्रयास किया समाधान जोड़े के लिए कोड में स्मृति स्थानों या पहचानकर्ता के जीवनकाल के लिए अन्य संसाधनों का जीवन है।

    +1

    आप यह उल्लेख करके उस फुटनोट को बेहतर बना सकते हैं कि यह गलत समाधान है! कवक और गैर-कवक वाली वस्तुओं को अलग-अलग संभालना होगा। –

    +0

    ईरविकियर: मैं आपसे सहमत हूं। हालांकि, चूंकि मुझे कोई भी भाषा नहीं है जो व्यवहार्य विकल्प लागू करती है, मुझे वास्तव में पता नहीं है कि बेहतर क्या होगा। खासकर जब से प्रत्येक संसाधन किसी पहचानकर्ता के लिए बाध्य होता है, और उस पहचानकर्ता के पास उसकी स्मृति के समान जीवनकाल होता है। –

    +0

    सी # का उपयोग करने वाला कीवर्ड एक व्यवहार्य विकल्प है: जब निष्पादन कोड ब्लॉक छोड़ देता है, तो संसाधन को मुक्त करने का समय आता है। यह गैर-कवक संसाधनों के लिए बेहतर है जो स्वतंत्रता की तरह कुछ कवक के लिए अपने जीवनकाल को बांधने से बेहतर है। –

    2

    साधारण स्पष्टीकरण:

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

    अंतिम रूप विधि को लागू करने के लिए कुछ दिशा-निर्देश:

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

    निपटान विधि को लागू करने के लिए कुछ दिशा-निर्देश:

    • एक प्रकार है कि संसाधन है कि स्पष्ट रूप से मुक्त किया जा करने की जरूरत है समाहित पर निपटाने डिज़ाइन पैटर्न को लागू करें।
    • आधार प्रकार पर निपटान डिज़ाइन पैटर्न को कार्यान्वित करें जिसमें संसाधनों पर एक या अधिक व्युत्पन्न प्रकार हैं, भले ही आधार प्रकार न हो।
    • उदाहरण के बाद निपटान को कॉल करने के बाद, अंतिम प्रक्रिया को GC.SuppressFinalize विधि को कॉल करके चलने से रोकें। इस नियम का एकमात्र अपवाद दुर्लभ स्थिति है जिसमें कार्य को अंतिम रूप देने के लिए किया जाना चाहिए जो निपटान द्वारा कवर नहीं किया गया है।
    • मान लीजिए कि निपटान कहा जाएगा। एक प्रकार के स्वामित्व वाले अप्रबंधित संसाधनों को उस घटना में अंतिम रूप में जारी किया जाना चाहिए, जिसे निपटान नहीं किया जाता है।
    • संसाधनों के पहले से निपटने के दौरान इस प्रकार (निपटान के अलावा) पर उदाहरण विधियों से ऑब्जेक्ट डिस्प्ले अपवाद को फेंक दें। यह नियम निपटान विधि पर लागू नहीं होता है क्योंकि इसे अपवाद फेंकने के बिना कई बार कॉल करने योग्य होना चाहिए।
    • बेस प्रकारों के पदानुक्रम के माध्यम से निपटने के लिए कॉल का प्रचार करें। निपटान विधि को इस ऑब्जेक्ट द्वारा आयोजित सभी संसाधनों और इस ऑब्जेक्ट के स्वामित्व वाले किसी ऑब्जेक्ट को मुक्त करना चाहिए।
    • आपको किसी ऑब्जेक्ट को कॉल करने के बाद किसी ऑब्जेक्ट को प्रयोग करने योग्य होने की अनुमति नहीं देना चाहिए। किसी ऑब्जेक्ट को दोबारा तैयार करना जिसे पहले से ही निपटाया गया है, लागू करने के लिए एक कठिन पैटर्न है।
    • एक अपवाद फेंकने के बिना एक निपटान विधि को एक से अधिक बार बुलाया जाने दें। विधि को पहली कॉल के बाद कुछ भी नहीं करना चाहिए।
    संबंधित मुद्दे