किसी वस्तु को अशक्त करना बनाम निपटाना ()


108

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

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

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

या, माईक्लास को आईडीसोपायरी और डिस्ट्रॉक्टर लागू करके और डिस्पोज () बताकर?

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

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

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

जवाबों:


210

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

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

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

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

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

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

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

एक चर सेट करना null

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

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 
    }
}

आईडीआईएसओपी / फाइनल को लागू करना

तो, क्या आपके अपने प्रकारों को अंतिम रूप देना चाहिए? लगभग निश्चित रूप से नहीं। यदि आप अप्रत्यक्ष रूप से अप्रबंधित संसाधनों को रखते हैं (जैसे कि आपको FileStreamएक सदस्य चर के रूप में मिला है ) तो अपने स्वयं के अंतिम रूप को जोड़ने से कोई मदद नहीं मिलेगी: धारा लगभग निश्चित रूप से कचरा संग्रह के लिए योग्य होगी जब आपकी वस्तु होगी, इसलिए आप बस भरोसा कर सकते हैं FileStreamफ़ाइनलीज़र होना (यदि आवश्यक हो - यह किसी और चीज़ आदि को संदर्भित कर सकता है)। आप एक अप्रबंधित संसाधन धारण करने के लिए चाहते हैं "लगभग" सीधे, SafeHandleअपने दोस्त है - यह समय के साथ जा रहा प्राप्त करने के लिए का एक सा लगता है, लेकिन यह आप लगाएंगे लगभग एक finalizer फिर से लिखने की ज़रूरत कभी नहीं । यदि आप किसी संसाधन (क IntPtr) पर वास्तव में प्रत्यक्ष नियंत्रण रखते हैं, तो आपको आम तौर पर केवल एक फाइनल की आवश्यकता होती है और आपको आगे बढ़ना चाहिएSafeHandleजितनी जल्दी आप कर सकते हों। (वहां दो लिंक हैं - दोनों पढ़ें, आदर्श रूप से।)

जो डफी के पास फ़ाइनलीज़र और आईडीसॉफ़ेरियल के आसपास दिशानिर्देशों का एक बहुत लंबा सेट है (स्मार्ट लोक के बहुत से सह-लिखित) जो पढ़ने लायक हैं। यह ध्यान देने योग्य है कि यदि आप अपनी कक्षाओं को सील करते हैं, तो यह जीवन को बहुत आसान बना देता है: Disposeएक नई आभासी Dispose(bool)विधि आदि को ओवरराइड करने का पैटर्न केवल तब प्रासंगिक होता है जब आपकी कक्षा विरासत के लिए डिज़ाइन की गई हो।

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


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

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

क्या आप बता सकते हैं कि जब आप अपने कोड स्निपेट में "foo = null" सेट करेंगे तो क्या होगा? जहां तक ​​मुझे पता है, वह लाइन केवल प्रबंधित हीप में foo ऑब्जेक्ट की ओर इशारा करते हुए एक वैरिएबल का मान साफ ​​करती है? तो सवाल यह है कि वहाँ वस्तु को लुभाने के लिए क्या होगा? क्या हमें इसका निपटान नहीं करना चाहिए?
ओडीज़

@odiseh: यदि वस्तु डिस्पोजेबल थी, तो हाँ - आपको इसे निपटाना चाहिए। उत्तर का वह खंड केवल कचरा संग्रहण से संबंधित था, जो पूरी तरह से अलग है।
जॉन स्कीट

1
मैं कुछ आईडीसोफ़रोबल चिंताओं पर स्पष्टीकरण की तलाश कर रहा था, इसलिए मैंने "आईडीआईसोफ़री स्कीट" के लिए गुगली ली और यह पाया। महान! : डी
मैकीज वोज्नियाक

22

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

myclass = null;

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


7
उस लाइन को निष्पादित करने के बाद भी यह मौजूद नहीं हो सकता है - यह उस लाइन से पहले एकत्र किया गया कचरा हो सकता है । JIT स्मार्ट है - इस तरह की लाइनें बनाना लगभग हमेशा अप्रासंगिक है।
जॉन स्कीट

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

6

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

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

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

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

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


मुझे लगता है कि यह उत्तर तारीफ या "पुनरावर्ती" के उत्तर का एक विवरण है।
नृत्य २
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.