2009-02-22 13 views
93

सीएलआर और जीसी काम करता है जिस तरह से मैं मोहक हूं (मैं सी # के माध्यम से सीएलआर, जॉन स्कीट की किताबों/पदों और अन्य के माध्यम से सीएलआर पढ़कर इस पर अपना ज्ञान विस्तार करने पर काम कर रहा हूं)।ऑब्जेक्ट को नल बनाम निपटाने के लिए सेट करना()

वैसे भी, कह रही है के बीच अंतर क्या है:

MyClass myclass = new MyClass(); 
myclass = null; 

या, MyClass लागू IDisposable और एक नाशक बनाने और निपटान को फोन करके()?

इसके अलावा, अगर मेरे पास एक कथन कथन (उदाहरण के लिए) के साथ कोड ब्लॉक है, तो यदि मैं कोड के माध्यम से कदम उठाता हूं और उपयोग ब्लॉक से बाहर निकलता हूं, तो क्या ऑब्जेक्ट तब कचरा संग्रह होता है जब कचरा संग्रह होता है? यदि मैं किसी भी ब्लॉक का उपयोग कर निपटान() को कॉल करता हूं तो क्या होगा?

using (MyDisposableObj mydispobj = new MyDisposableObj()) 
{ 

} 

स्ट्रीम कक्षाएं (उदाहरण के लिए बाइनरीवाइटर) में एक अंतिम विधि है? मैं इसका उपयोग क्यों करना चाहूंगा?

उत्तर

185

कचरा संग्रह से निपटान को अलग करना महत्वपूर्ण है। वे पूरी तरह से अलग चीजें हैं, एक बिंदु में आम है जो मैं एक मिनट में आऊंगा।

Dispose, कचरा संग्रहण और अंतिम रूप दिए जाने

जब आप एक using बयान लिखने, यह बस वाक्यात्मक चीनी एक कोशिश/अंत में ब्लॉक के लिए है ताकि Dispose कहा जाता है, भले ही using बयान के शरीर में कोड फेंकता एक अपवाद। यह का अर्थ यह नहीं है कि वस्तु ब्लॉक के अंत में कचरा एकत्रित है।

निपटान अप्रबंधित संसाधन (गैर-स्मृति संसाधन) है। ये यूआई हैंडल, नेटवर्क कनेक्शन, फाइल हैंडल इत्यादि हो सकते हैं। ये सीमित संसाधन हैं, इसलिए आप उन्हें जितनी जल्दी हो सके उन्हें छोड़ना चाहते हैं। आपको IDisposable लागू करना चाहिए जब भी आपका प्रकार एक अप्रबंधित संसाधन "मालिक" हो, सीधे (आमतौर पर IntPtr के माध्यम से) या अप्रत्यक्ष रूप से (उदा। Stream, SqlConnection आदि के माध्यम से)।

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

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

फ़ाइनलाइज़र लगभग हमेशा उस मामले में संसाधनों को साफ करने के लिए उपयोग किए जाते हैं जहां प्रकार का उपयोगकर्ता व्यवस्थित ढंग से इसका निपटान करने के लिए भूल गया है। तो यदि आप FileStream खोलते हैं लेकिन Dispose या Close पर कॉल करना भूल जाते हैं, तो अंतिम अंततः आपके लिए अंतर्निहित फ़ाइल हैंडल जारी करेगा। एक अच्छी तरह से लिखित कार्यक्रम में, फाइनलरों को मेरी राय में लगभग कभी नहीं आग लगनी चाहिए।

null करने के लिए एक चर की स्थापना पर null

एक छोटी सी बात करने के लिए एक चर सेट करना - यह लगभग कचरा संग्रहण के लिए आवश्यक कभी नहीं किया गया है। यदि आप एक सदस्य चर है, तो आप कभी-कभी ऐसा करना चाहते हैं, हालांकि मेरे अनुभव में किसी ऑब्जेक्ट के "भाग" के लिए दुर्लभ होना आवश्यक नहीं है। जब यह एक स्थानीय चर है, तो यह जानने के लिए कि जब आप किसी संदर्भ का उपयोग नहीं करेंगे तो JIT आमतौर पर पर्याप्त (रिलीज़ मोड में) स्मार्ट होगा। उदाहरण के लिए:

StringBuilder sb = new StringBuilder(); 
sb.Append("Foo"); 
string x = sb.ToString(); 

// The string and StringBuilder are already eligible 
// for garbage collection here! 
int y = 10; 
DoSomething(y); 

// These aren't helping at all! 
x = null; 
sb = null; 

// Assume that x and sb aren't used here 

एक बार जहां यह null के लिए एक स्थानीय चर स्थापित कर रही है जब आप एक पाश में हैं, और पाश की कुछ शाखाओं चर का उपयोग करने की जरूरत है, लेकिन आप क्या आप जानते हैं लायक हो सकता है एक बिंदु पर पहुंचा है जिस पर आप नहीं करते हैं। उदाहरण के लिए:

SomeObject foo = new SomeObject(); 

for (int i=0; i < 100000; i++) 
{ 
    if (i == 5) 
    { 
     foo.DoSomething(); 
     // We're not going to need it again, but the JIT 
     // wouldn't spot that 
     foo = null; 
    } 
    else 
    { 
     // Some other code 
    } 
} 

कार्यान्वयन IDisposable/finalizers

तो, अपने स्वयं के प्रकार finalizers को लागू करना चाहिए? लगभग निश्चित रूप से नहीं। यदि आप केवल परोक्ष रूप से अप्रबंधित संसाधन (उदा।आपके पास सदस्य चर के रूप में FileStream है) फिर अपना स्वयं का फ़ाइनलाइज़र जोड़ने में मदद नहीं करेगा: आपकी ऑब्जेक्ट होने पर धारा लगभग निश्चित रूप से कचरा संग्रह के लिए योग्य होगी, इसलिए आप केवल अंतिम बारिश करने वाले FileStream पर भरोसा कर सकते हैं (यदि आवश्यक हो - यह कुछ और, आदि का उल्लेख कर सकता है)। यदि आप एक अप्रबंधित संसाधन "लगभग" सीधे पकड़ना चाहते हैं, तो SafeHandle आपका मित्र है - इसमें जाने में थोड़ा समय लगता है, लेकिन इसका मतलब है कि आप almostnever need to write a finalizer again देखेंगे। यदि आपको संसाधन (वास्तव में IntPtr) पर वास्तव में प्रत्यक्ष संभाल है तो आपको केवल अंतिम रूप देने की आवश्यकता होनी चाहिए और आपको जितनी जल्दी हो सके SafeHandle पर जाना चाहिए। (वहां दो लिंक हैं - आदर्श रूप से पढ़ें।)

जो डफी के पास very long set of guidelines around finalizers and IDisposable (बहुत से स्मार्ट लोक के साथ सह-लिखित) है जो पढ़ने योग्य हैं। यह जानना उचित है कि यदि आप अपनी कक्षाओं को सील करते हैं, तो यह जीवन को बहुत आसान बनाता है: Dispose ओवरराइड करने का पैटर्न एक नया आभासी Dispose(bool) विधि इत्यादि को कॉल करने के लिए केवल तभी प्रासंगिक होता है जब आपकी कक्षा विरासत के लिए डिज़ाइन की जाती है।

यह एक पर्यटन का एक सा है, लेकिन स्पष्टीकरण के लिए कहें आप जहां चाहें कुछ :)

+0

पुन "एक बार जहां स्थानीय चर को शून्य करने के लायक हो सकता है" - शायद कुछ कांटेदार "कैप्चर" परिदृश्य (समान चर के एकाधिक कैप्चर) - लेकिन यह पोस्ट को जटिल बनाने के लायक नहीं हो सकता है! +1 ... –

+0

@Marc: यह सच है - मैंने कब्जे वाले चर के बारे में भी सोचा नहीं था। हम्म। हाँ, मुझे लगता है कि मैं अकेला छोड़ दूंगा;) –

+9

वाह। मैं अंततः स्कीटिस्ट्स की पंथ के स्रोत को समझता हूं। यह पोस्ट कमाल है! – JohnFx

19

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

myclass = null; 

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

+7

यह * * अभी भी उस लाइन को क्रियान्वित करने के बाद अस्तित्व में नहीं कर सकते हैं - यह कचरा एकत्र किया गया है हो सकता है * पहले * वह रेखा जेआईटी स्मार्ट बनाने वाली लाइनें लगभग हमेशा अप्रासंगिक है। –

+6

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

4

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

जब आप निपटान() कहते हैं, तो यह ऑब्जेक्ट पर एक विधि कॉल है। जो भी निपटान विधि करता है, अब ऑब्जेक्ट पर किया जाता है। लेकिन यह वस्तु के आपके संदर्भ को प्रभावित नहीं करता है।

ओवरलैप के केवल क्षेत्र है कि जब वहाँ एक वस्तु के लिए कोई अधिक संदर्भ हैं, यह अंततः कचरा एकत्र हो जाएगा है। और यदि कक्षा IDISposable इंटरफेस लागू करती है, तो ऑब्जेक्ट पर कचरा() को कचरा इकट्ठा करने से पहले कहा जाएगा।

लेकिन यह दो कारणों से, शून्य के संदर्भ को सेट करने के तुरंत बाद नहीं होगा। सबसे पहले, अन्य संदर्भ मौजूद हो सकते हैं, इसलिए इसे अभी तक कचरा नहीं मिलेगा, और दूसरा, भले ही वह अंतिम संदर्भ था, इसलिए अब यह कचरा इकट्ठा करने के लिए तैयार है, कचरा कलेक्टर का फैसला होने तक कुछ भी नहीं होगा ऑब्जेक्ट हटाएं।

किसी ऑब्जेक्ट पर कॉलिंग डिस्प्ले() ऑब्जेक्ट को किसी भी तरह से "मार" नहीं देता है। इसका उपयोग आमतौर पर साफ करने के लिए किया जाता है ताकि ऑब्जेक्ट बाद में सुरक्षित रूप से हटाया जा सके, लेकिन आखिरकार, निपटान के बारे में जादुई कुछ भी नहीं है, यह सिर्फ एक वर्ग विधि है।

+0

मुझे लगता है कि यह उत्तर प्रशंसा या "रिकर्सिव" के उत्तर के लिए एक विवरण है। – Sung

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^