क्या आपको वस्तुओं का निपटान करने और उन्हें अशक्त करने की आवश्यकता है?


310

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


4
ऐसा प्रतीत होता है कि सर्वसम्मति से आपको ऑब्जेक्ट को शून्य करने की आवश्यकता नहीं है, लेकिन क्या आपको डिस्पोज़ () करने की आवश्यकता है?
CJ7

3
जैसा कि जेफ ने कहा: कोडिंगहोरर.com
blog

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

11
@peter: WCF क्लाइंट प्रॉक्सिस के
nlawalker

9
फिर भी, आप अपनी Dispose()विधि के अंदर अशक्त करने के लिए कुछ संदर्भ सेट करना चाहते हैं ! यह इस सवाल पर एक सूक्ष्म बदलाव है, लेकिन महत्वपूर्ण है क्योंकि निपटाए जा रहे ऑब्जेक्ट को यह पता नहीं चल सकता है कि क्या यह "दायरे से बाहर जा रहा है" (कॉलिंग Dispose()कोई गारंटी नहीं है)। यहाँ और अधिक: stackoverflow.com/questions/6757048/…
केविन पी। चावल

जवाबों:


239

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

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

यदि usingआपका प्रोग्राम usingकथन का दायरा छोड़ देता है, तो आप किसी वस्तु को स्वचालित रूप से डिस्पोज़ करने के लिए स्टेटमेंट का उपयोग कर सकते हैं ।

using (MyIDisposableObject obj = new MyIDisposableObject())
{
    // use the object here
} // the object is disposed here

जो कार्यात्मक रूप से इसके बराबर है:

MyIDisposableObject obj;
try
{
    obj = new MyIDisposableObject();
}
finally
{
    if (obj != null)
    {
        ((IDisposable)obj).Dispose();
    }
}

4
यदि ओब्ज एक संदर्भ प्रकार है तो अंत में ब्लॉक के बराबर है:if (obj != null) ((IDisposable)obj).Dispose();
रैंडी

1
@Tuzo: धन्यवाद! को प्रतिबिंबित करने के लिए संपादित।
जैच जॉनसन

2
के बारे में एक टिप्पणी IDisposable। किसी वस्तु को डिस्पोज़ करने में विफल होना आमतौर पर किसी भी अच्छी तरह से डिज़ाइन किए गए वर्ग पर स्मृति रिसाव का कारण नहीं होगा । जब C # में अप्रबंधित संसाधनों के साथ काम करना हो, तो आपके पास एक अंतिम उपकरण होना चाहिए जो अभी भी अप्रबंधित संसाधनों को जारी करेगा। इसका मतलब यह है कि जब यह किया जाना चाहिए, तो संसाधनों से निपटने के बजाय, जब कचरा संग्रहकर्ता प्रबंधित वस्तु को अंतिम रूप देता है, तो इसे स्थगित कर दिया जाएगा। यह अभी भी कई अन्य मुद्दों का कारण बन सकता है (जैसे कि असंबंधित ताले)। आपको IDisposableहालांकि निपटाना चाहिए !
Aidiakapi

@RandyLevy क्या आपके पास इसके लिए कोई संदर्भ है? साभार
बेसिक

लेकिन मेरा सवाल डिस्पोज़ () किसी भी तर्क को लागू करने की आवश्यकता है? क्या इसके लिए कुछ करना होगा? या आंतरिक रूप से जब डिस्पोज़ () को सिग्नल कहा जाता है GC जो जाना अच्छा है? मैंने उदाहरण के लिए TextWriter के स्रोत कोड की जाँच की और डिस्पोज़ का कोई कार्यान्वयन नहीं है।
मिहेल जॉर्जेसकु

137

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

एक वस्तु एक फ़ंक्शन में "स्कोप से बाहर" जा सकती है, लेकिन यदि उसका मान वापस आ जाता है, तो जीसी यह देखेगा कि कॉलिंग फ़ंक्शन रिटर्न वैल्यू पर है या नहीं।

ऑब्जेक्ट संदर्भ सेट करना null अनावश्यक है क्योंकि कचरा संग्रह काम करता है जो वस्तुओं को अन्य वस्तुओं द्वारा संदर्भित किया जाता है।

व्यवहार में, आपको विनाश के बारे में चिंता करने की ज़रूरत नहीं है, यह सिर्फ काम करता है और यह बहुत अच्छा है :)

Disposeउन सभी ऑब्जेक्ट्स पर कॉल किया जाना चाहिए जो IDisposableतब लागू होते हैं जब आप उनके साथ काम कर रहे होते हैं। आम तौर पर आप usingउन वस्तुओं के साथ एक ब्लॉक का उपयोग करेंगे जैसे:

using (var ms = new MemoryStream()) {
  //...
}

संपादित करेंचर गुंजाइश पर । क्रेग ने पूछा है कि क्या चर दायरे का वस्तु जीवनकाल पर कोई प्रभाव पड़ता है। सीएलआर के उस पहलू को ठीक से समझाने के लिए, मुझे C ++ और C # से कुछ अवधारणाओं को समझाने की आवश्यकता होगी।

वास्तविक चर गुंजाइश

दोनों भाषाओं में चर का उपयोग केवल उसी दायरे में किया जा सकता है जब इसे परिभाषित किया गया था - वर्ग, कार्य या ब्रेस द्वारा संलग्न एक स्टेटमेंट ब्लॉक। हालाँकि, सूक्ष्म अंतर यह है कि C # में, चरों को एक नेस्टेड ब्लॉक में पुनर्परिभाषित नहीं किया जा सकता है।

C ++ में, यह पूरी तरह से कानूनी है:

int iVal = 8;
//iVal == 8
if (iVal == 8){
    int iVal = 5;
    //iVal == 5
}
//iVal == 8

C # में, हालांकि आपको एक कंपाइलर त्रुटि मिलती है:

int iVal = 8;
if(iVal == 8) {
    int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}

यह समझ में आता है यदि आप उत्पन्न MSIL को देखते हैं - फ़ंक्शन द्वारा उपयोग किए जाने वाले सभी चर फ़ंक्शन के प्रारंभ में परिभाषित किए गए हैं। इस समारोह पर एक नज़र डालें:

public static void Scope() {
    int iVal = 8;
    if(iVal == 8) {
        int iVal2 = 5;
    }
}

नीचे उत्पन्न आईएल है। ध्यान दें कि iVal2, जिसे अगर ब्लॉक के अंदर परिभाषित किया गया है, तो वास्तव में फ़ंक्शन स्तर पर परिभाषित किया गया है। प्रभावी रूप से इसका मतलब है कि C # में केवल वर्ग और फ़ंक्शन स्तर की गुंजाइश है जहां तक ​​चर जीवनकाल का संबंध है।

.method public hidebysig static void  Scope() cil managed
{
  // Code size       19 (0x13)
  .maxstack  2
  .locals init ([0] int32 iVal,
           [1] int32 iVal2,
           [2] bool CS$4$0000)

//Function IL - omitted
} // end of method Test2::Scope

सी ++ गुंजाइश और वस्तु जीवनकाल

जब भी C ++ वैरिएबल, स्टैक पर आवंटित किया जाता है, तो वह स्कोप से बाहर हो जाता है और वह नष्ट हो जाता है। याद रखें कि C ++ में आप स्टैक पर या ढेर पर ऑब्जेक्ट बना सकते हैं। जब आप उन्हें स्टैक पर बनाते हैं, तो एक बार निष्पादन गुंजाइश छोड़ देता है, वे स्टैक से पॉपप हो जाते हैं और नष्ट हो जाते हैं।

if (true) {
  MyClass stackObj; //created on the stack
  MyClass heapObj = new MyClass(); //created on the heap
  obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives

जब सी ++ ऑब्जेक्ट्स को ढेर पर बनाया जाता है, तो उन्हें स्पष्ट रूप से नष्ट कर दिया जाना चाहिए, अन्यथा यह एक मेमोरी लीक है। हालांकि स्टैक चर के साथ ऐसी कोई समस्या नहीं है।

सी # ऑब्जेक्ट लाइफटाइम

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

MyClass stackObj;

C ++ में यह MyClassस्टैक पर एक इंस्टेंस बनाएगा और इसके डिफॉल्ट कंस्ट्रक्टर को कॉल करेगा। C # में यह वर्ग के लिए एक संदर्भ पैदा करेगा MyClassजो किसी भी चीज़ की ओर इशारा नहीं करता है। newऑपरेटर का उपयोग करके किसी वर्ग की आवृत्ति बनाने का एकमात्र तरीका है :

MyClass stackObj = new MyClass();

एक तरह से, C # ऑब्जेक्ट बहुत सी ऐसी वस्तुएं हैं जो newC ++ में सिंटैक्स का उपयोग करके बनाई गई हैं - वे ढेर पर बनाई गई हैं लेकिन C ++ ऑब्जेक्ट्स के विपरीत, वे रनटाइम द्वारा प्रबंधित की जाती हैं, इसलिए आपको उन्हें नष्ट करने के बारे में चिंता करने की ज़रूरत नहीं है।

चूँकि वस्तुएं हमेशा इस तथ्य पर होती हैं कि ऑब्जेक्ट रेफ़रेंस (यानी पॉइंटर्स) दायरे से बाहर हो जाते हैं, तो वे मूट हो जाते हैं। यह निर्धारित करने में अधिक कारक शामिल हैं कि क्या किसी वस्तु को केवल वस्तु के संदर्भ में मौजूदगी से एकत्र किया जाए।

C # ऑब्जेक्ट संदर्भ

जॉन स्कीट ने जावा में ऑब्जेक्ट के संदर्भों की तुलना स्ट्रिंग के टुकड़ों से की जो गुब्बारे से जुड़े होते हैं, जो कि वस्तु है। समान सादृश्य C # ऑब्जेक्ट संदर्भों पर लागू होता है। वे बस ढेर के एक स्थान को इंगित करते हैं जिसमें ऑब्जेक्ट होता है। इस प्रकार, इसे शून्य पर सेट करने से वस्तु के जीवनकाल पर कोई तत्काल प्रभाव नहीं पड़ता है, गुब्बारा मौजूद रहता है, जब तक कि जीसी "पॉप" नहीं करता।

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

.NET ऑब्जेक्ट्स एक छत के नीचे हीलियम गुब्बारे की तरह हैं। जब छत खुलती है (जीसी चलता है) - अप्रयुक्त गुब्बारे दूर तैरते हैं, भले ही गुब्बारे के समूह हो सकते हैं जो एक साथ मिलकर होते हैं।

.NET GC, जेनेशनल GC और मार्क और स्वीप के संयोजन का उपयोग करता है। पीढ़ीगत दृष्टिकोण में उन वस्तुओं का निरीक्षण करने के लिए रनटाइम का पक्ष शामिल है जिन्हें हाल ही में आवंटित किया गया है, क्योंकि वे अप्रयुक्त होने की अधिक संभावना है और मार्क और स्वीप में रनटाइम पूरे ऑब्जेक्ट ग्राफ के माध्यम से जा रहा है और अगर ऑब्जेक्ट ऑब्जेक्ट्स अप्रयुक्त हैं तो बाहर काम करना शामिल है। यह पर्याप्त रूप से परिपत्र निर्भरता समस्या से संबंधित है।

इसके अलावा, .NET GC दूसरे थ्रेड (जिसे अंतिम रूप से थ्रेड कहा जाता है) पर चलता है क्योंकि इसमें मुख्य थ्रेड पर ऐसा करने और करने के लिए काफी कुछ है जो आपके प्रोग्राम को बाधित करेगा।


1
@ आईओआरजी: 'दायरे से बाहर' जाने का मतलब है कि वस्तु संदर्भ संदर्भ से बाहर है और वर्तमान दायरे में संदर्भित नहीं किया जा सकता है। निश्चित रूप से यह अभी भी C # में होता है।
सीजे 7

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

1
@ क्रैग जॉनसन: blogs.msdn.com/b/ericgu/archive/2004/07/23/192842.aspx देखें : "इस बात की कोई गारंटी नहीं है कि एक स्थानीय चर एक दायरे के अंत तक जीवित रहेगा यदि यह नहीं है उपयोग किया जाता है। रनटाइम उस कोड का विश्लेषण करने के लिए स्वतंत्र है जो यह निर्धारित करता है कि एक निश्चित बिंदु से परे चर का कोई और उपयोग नहीं है, और इसलिए उस चर को उस बिंदु से परे नहीं रखें (अर्थात इसे उद्देश्यों के लिए मूल के रूप में नहीं मानें। GC की)
रैंडी

1
@ तुजो: सच। यही GC.KeepAlive के लिए है।
स्टीवन सुमित

1
@ क्रेग जॉनसन: नहीं और हाँ। नहीं। क्योंकि .NET रनटाइम आपके लिए काम करता है और एक अच्छा काम करता है। हां, क्योंकि प्रोग्रामर का काम कोड लिखना नहीं है जो (सिर्फ) संकलन करता है, लेकिन उस कोड को लिखना है जो चलता है । कभी-कभी यह जानने में मदद मिलती है कि कवर के तहत रनटाइम क्या कर रहा है (जैसे समस्या निवारण)। कोई यह तर्क दे सकता है कि यह ज्ञान का प्रकार है जो अच्छे प्रोग्रामर को महान प्रोग्रामर से अलग करने में मदद करता है।
रैंडी

18

जैसा कि अन्य लोगों ने कहा है कि आप निश्चित रूप से कॉल करना चाहते हैं Disposeयदि क्लास लागू होती है IDisposable। मैं इस पर काफी कठोर स्थिति लेता हूं। कुछ हो सकता है दावा बुला कि Disposeपर DataSet, उदाहरण के लिए, व्यर्थ क्योंकि वे इसे disassembled और देखा है कि यह कुछ भी सार्थक नहीं किया है। लेकिन, मुझे लगता है कि उस तर्क में कुछ गलतियां हैं।

इस विषय पर सम्मानित व्यक्तियों द्वारा एक दिलचस्प बहस के लिए पढ़ें । फिर मेरे तर्क को यहाँ पढ़ें कि मुझे क्यों लगता है कि जेफ़री रिक्टर गलत शिविर में है।

अब, आपको एक संदर्भ सेट करना चाहिए या नहीं null। जवाब न है। मुझे निम्नलिखित कोड के साथ अपनी बात समझाना चाहिए।

public static void Main()
{
  Object a = new Object();
  Console.WriteLine("object created");
  DoSomething(a);
  Console.WriteLine("object used");
  a = null;
  Console.WriteLine("reference set to null");
}

तो आपको कब लगता है कि संदर्भित वस्तु aसंग्रह के योग्य है? अगर आपने कॉल के बाद कहा a = nullतो आप गलत हैं। अगर आपने Mainविधि पूरी होने के बाद कहा तो आप भी गलत हैं। सही उत्तर यह है कि यह कॉल करने के दौरान कुछ समय के लिए संग्रह के योग्य है DoSomething। यह सही है। यह संदर्भ के सेट होने से पहले ही योग्य है nullऔर शायद कॉल DoSomethingपूरा होने से पहले भी । ऐसा इसलिए है क्योंकि JIT कंपाइलर पहचान सकता है जब ऑब्जेक्ट रेफरेंस को अब भी डिरेल नहीं किया जाता है, भले ही वे अभी भी निहित हों।


3
क्या होगा यदि aएक वर्ग में एक निजी सदस्य क्षेत्र है? यदि aजीसी को शून्य करने के लिए सेट नहीं किया गया है, तो aयह जानने का कोई तरीका नहीं है कि क्या किसी विधि में फिर से उपयोग किया जाएगा, है ना? इस प्रकार aएकत्र नहीं किया जाएगा जब तक कि पूरी युक्त कक्षा एकत्र न हो जाए। नहीं?
केविन पी। चावल

4
@ केविन: सही है। यदि aएक वर्ग के सदस्य और वर्ग युक्त वर्ग aअभी भी निहित और उपयोग में था तो यह भी घूम जाएगा। यह एक ऐसा परिदृश्य है जहां इसे स्थापित करना nullफायदेमंद हो सकता है।
ब्रायन गिदोन

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

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

13

आपको कभी भी C # में वस्तुओं को सेट करने की आवश्यकता नहीं है। कंपाइलर और रनटाइम इस बात का ध्यान रखेंगे कि जब वे दायरे में न हों।

हां, आपको उन वस्तुओं का निपटान करना चाहिए जो आईडीआईसोपायरी को लागू करते हैं।


2
यदि आपके पास एक बड़ी वस्तु के लिए लंबे समय तक रहने वाला (या यहां तक ​​कि स्थिर) संदर्भ है, तो आप wantइसे जल्द से जल्द समाप्त कर देंगे, ताकि आप इसे दोबारा प्राप्त कर सकें।
स्टीवन सुमित

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

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

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

2
आप इसे संसाधित करने वाली विधि में एक स्थानीय चर में कच्चे डेटा को संग्रहीत करने से बचें। विधि संसाधित डेटा लौटाती है, जिसे आप रखते हैं, लेकिन कच्चे डेटा के लिए स्थानीय कार्यक्षेत्र से बाहर हो जाता है जब विधि बाहर निकल जाती है और स्वचालित रूप से GCed हो जाती है।
ईएमपी

11

मैं यहाँ आम जवाब से सहमत हूँ कि हाँ आपको निपटाना चाहिए और नहीं, आप आम तौर पर चर को शून्य करने के लिए सेट नहीं करना चाहिए ... लेकिन मैं यह बताना चाहता था कि निपटान मुख्य रूप से स्मृति प्रबंधन के बारे में नहीं है। हाँ, यह स्मृति प्रबंधन के साथ (और कभी-कभी करता है) मदद कर सकता है, लेकिन यह प्राथमिक उद्देश्य है कि आप दुर्लभ संसाधनों को निर्धारक जारी करें।

उदाहरण के लिए, यदि आप एक हार्डवेयर पोर्ट (उदाहरण के लिए सीरियल), एक टीसीपी / आईपी सॉकेट, एक फाइल (एक्सक्लूसिव एक्सेस मोड) या यहां तक ​​कि एक डेटाबेस कनेक्शन खोलते हैं, तो आपने अब तक किसी भी अन्य कोड को उन वस्तुओं का उपयोग करने से रोका है जब तक वे जारी नहीं होते हैं। डिस्पोज़ आम तौर पर इन वस्तुओं (जीडीआई और अन्य "ओएस" हैंडल आदि के साथ जारी करता है जो 1000 उपलब्ध हैं, लेकिन अभी भी समग्र रूप से सीमित हैं)। यदि आप स्वामी ऑब्जेक्ट पर डिपोज़ को कॉल नहीं करते हैं और इन संसाधनों को स्पष्ट रूप से जारी करते हैं, तो भविष्य में उसी संसाधन को फिर से खोलने का प्रयास करें (या कोई अन्य प्रोग्राम करता है) जो खुला प्रयास विफल हो जाएगा, क्योंकि आपका अपरिष्कृत, अनियंत्रित ऑब्जेक्ट अभी भी आइटम खुला है । बेशक, जब जीसी आइटम एकत्र करता है (यदि निपटान पैटर्न सही तरीके से लागू किया गया है) तो संसाधन जारी हो जाएगा ... लेकिन आपको नहीं पता कि यह कब होगा, इसलिए आप डॉन ' t पता है कि उस संसाधन को फिर से खोलना कब सुरक्षित है। यह प्राथमिक मुद्दा है डिस्पोजल काम करता है। बेशक, इन हैंडल को रिलीज़ करने से अक्सर मेमोरी भी रिलीज़ होती है, और कभी भी रिलीज़ नहीं होने से ये मेमोरी कभी रिलीज़ नहीं हो सकती ... इसलिए मेमोरी लीक या मेमोरी में देरी के बारे में सभी बातें साफ हो जाती हैं।

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


11

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

MSDN पर एक निपटान पद्धति को लागू करना भी देखें ।


लेकिन गैराज कलेक्टर कॉल डिस्पोज़ () नहीं करेगा? यदि हां, तो आपको इसे कॉल करने की आवश्यकता क्यों होगी?
CJ7

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

12
GC कभी भी Dispose () नहीं कहेगा। जीसी अंतिम रूप से कह सकता है कि सम्मेलन को संसाधनों को साफ करना चाहिए।
एड्रियनम

@ एड्रियनम: mightकॉल नहीं , बल्कि willकॉल।
लेप्पी

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

9

यदि वे आईडीआईसॉपी इंटरफ़ेस लागू करते हैं तो आपको उन्हें निपटाना चाहिए। कूड़ा उठाने वाले बाकी की देखभाल करेंगे।

EDIT:using डिस्पोजेबल आइटम के साथ काम करते समय कमांड का उपयोग करना सबसे अच्छा है :

using(var con = new SqlConnection("..")){ ...

5

जब कोई ऑब्जेक्ट IDisposableआपको लागू करना चाहिए Dispose(या Close, कुछ मामलों में, जिसे आप के लिए निपटान कहेंगे)।

आपको सामान्य रूप से वस्तुओं को सेट करने की आवश्यकता नहीं है null, क्योंकि जीसी को पता चल जाएगा कि अब किसी वस्तु का उपयोग नहीं किया जाएगा।

जब मैं ऑब्जेक्ट सेट करता हूं तो एक अपवाद होता है null। जब मैं बहुत सारी वस्तुओं (डेटाबेस से) को प्राप्त करता हूं, जिस पर मुझे काम करने की आवश्यकता होती है, और उन्हें एक संग्रह (या सरणी) में संग्रहीत करता है। जब "काम" किया जाता है, तो मैंने ऑब्जेक्ट को सेट कर दिया null, क्योंकि जीसी को नहीं पता कि मैं इसके साथ काम कर रहा हूं।

उदाहरण:

using (var db = GetDatabase()) {
    // Retrieves array of keys
    var keys = db.GetRecords(mySelection); 

    for(int i = 0; i < keys.Length; i++) {
       var record = db.GetRecord(keys[i]);
       record.DoWork();
       keys[i] = null; // GC can dispose of key now
       // The record had gone out of scope automatically, 
       // and does not need any special treatment
    }
} // end using => db.Dispose is called

4

आम तौर पर, फ़ील्ड को शून्य करने के लिए सेट करने की कोई आवश्यकता नहीं है। मैं हमेशा अप्रबंधित संसाधनों को निपटाने की सलाह दूंगा।

अनुभव से मैं आपको निम्नलिखित करने की सलाह भी दूंगा:

  • यदि आपको अब उनकी आवश्यकता नहीं है, तो घटनाओं से सदस्यता समाप्त करें।
  • यदि कोई ज़रूरत नहीं है, तो किसी प्रतिनिधि या अभिव्यक्ति को अशक्त करने के लिए कोई भी फ़ील्ड सेट करें।

मैं उन मुद्दों को खोजने के लिए बहुत मुश्किल से आया हूं जो ऊपर दी गई सलाह का पालन न करने का प्रत्यक्ष परिणाम थे।

ऐसा करने के लिए एक अच्छी जगह डिस्पोज़ () में है, लेकिन जल्द ही आमतौर पर बेहतर होता है।

सामान्य तौर पर, यदि किसी वस्तु के संदर्भ में कचरा संग्रहकर्ता (GC) मौजूद होता है, तो यह पता लगाने में अधिक समय लग सकता है कि एक वस्तु अब उपयोग में नहीं है। स्मृति में रहते हुए सभी वस्तु।

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


1
'घटनाओं और प्रतिनिधियों' के बारे में आपका क्या मतलब है - इनसे क्या 'साफ' होना चाहिए?
CJ7

@ क्रेग - मैंने अपना उत्तर संपादित किया। उम्मीद है कि यह इसे थोड़ा स्पष्ट करता है।
Marnix van Valen

3

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

लीपी मत सुनो।

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

एक चीज जो हो सकती है वह यह है कि बहुत सी वस्तुओं को खुला रखा जा सकता है। यह आपके एप्लिकेशन की मेमोरी उपयोग को बहुत बढ़ा सकता है। कभी-कभी यह काम करना मुश्किल होता है कि क्या यह वास्तव में मेमोरी लीक है, या क्या आपका आवेदन केवल बहुत सारा सामान कर रहा है।

मेमोरी प्रोफाइल टूल जैसी चीजों के साथ मदद कर सकता है, लेकिन यह मुश्किल हो सकता है।

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

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


2

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

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