क्या गार्बेज कलेक्टर मेरे लिए IDisposable.Dispose कॉल करेगा?


134

.NET IDisposable पैटर्न का तात्पर्य है कि यदि आप एक अंतिम रूप से लिखते हैं, और IDis प्रयोज्य को लागू करते हैं, तो आपके अंतिम रूप से स्पष्ट रूप से प्रस्ताव का उपयोग करने की आवश्यकता है। यह तार्किक है, और मैंने हमेशा उन दुर्लभ स्थितियों में किया है जहां एक फाइनल में वारंट होता है।

हालाँकि, अगर मैं ऐसा करूँ तो क्या होगा:

class Foo : IDisposable
{
     public void Dispose(){ CloseSomeHandle(); }
}

और एक अंतिम, या कुछ भी लागू नहीं करते हैं। क्या फ्रेमवर्क मेरे लिए डिस्पोज़ विधि कहेगा?

हाँ, मुझे लगता है कि यह गूंगा लगता है, और सभी तर्क यह मानते हैं कि यह नहीं होगा, लेकिन मैंने हमेशा अपने सिर के पीछे 2 चीजें की हैं जिन्होंने मुझे अनिश्चित बना दिया है।

  1. कुछ साल पहले किसी ने एक बार मुझसे कहा था कि यह वास्तव में ऐसा करेगा, और उस व्यक्ति के पास "सामान को जानने" का बहुत ठोस ट्रैक रिकॉर्ड था।

  2. संकलक / ढांचा आपके द्वारा लागू किए गए इंटरफेस के आधार पर अन्य 'जादू' चीजें करता है (उदाहरण के लिए: फ़ॉरचेक, एक्सटेंशन विधियां, विशेषताओं के आधार पर क्रमांकन आदि), इसलिए यह समझ में आता है कि यह 'जादू' भी हो सकता है।

जब मैंने इसके बारे में बहुत कुछ पढ़ा है, और इसमें बहुत सी चीजें निहित हैं, तो मैं कभी भी इस प्रश्न का कोई निश्चित हां या नहीं का जवाब नहीं पा सका हूं ।

जवाबों:


121

.नेट गारबेज कलेक्टर, ऑब्जेक्ट को कॉल करता है। कचरा संग्रहण पर किसी ऑब्जेक्ट का विधि विधि। द्वारा डिफ़ॉल्ट करता है कुछ भी नहीं है और overidden होना चाहिए अगर आप अतिरिक्त संसाधनों मुक्त करना चाहते हैं।

निपटान स्वचालित रूप से नहीं कहा जाता है और किया जाना चाहिए स्पष्ट रूप से इस तरह के एक 'का उपयोग कर' या 'अंत में कोशिश' ब्लॉक के भीतर के रूप में संसाधनों जारी होने की कर रहे हैं कहा जाता है,

देख http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx अधिक जानकारी के लिए


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

7
MSDN के अनुसार: msdn.microsoft.com/en-us/library/… आप वास्तव में C # में Object.Finalize विधि को "ओवरराइड" नहीं कर सकते, कंपाइलर एक त्रुटि उत्पन्न करता है: ऑब्जेक्ट को ओवरराइड न करें। इसके बजाय, एक विध्वंसक प्रदान करते हैं। ; यानी आपको एक विध्वंसक को लागू करना होगा जो अंतिम रूप से प्रभावी रूप से कार्य करता है। [अभी पूर्णता के लिए यहां जोड़ा गया है क्योंकि यह स्वीकृत उत्तर है और सबसे अधिक पढ़ने की संभावना है]
सुधांशु मिश्रा

1
जीसी एक ऑब्जेक्ट के लिए कुछ भी नहीं करता है जो एक अंतिम रूप से ओवरराइड नहीं करता है। इसे अंतिम रूप देने की कतार में नहीं लगाया जाता है - और कोई भी अंतिम रूप नहीं कहा जाता है।
डेव ब्लैक

1
@dotnetguy - भले ही मूल C # कल्पना में "विध्वंसक" का उल्लेख है, लेकिन इसे वास्तव में अंतिम रूप दिया जाता है - और यह यांत्रिकी पूरी तरह से अलग है कि कैसे एक सच्चा "विध्वंसक" मानव रहित भाषाओं के लिए काम करता है।
डेव ब्लैक

67

मैं अपनी टिप्पणी में ब्रायन की बात पर जोर देना चाहता हूं, क्योंकि यह महत्वपूर्ण है।

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

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

जैसा कि आप जानते हैं या नहीं कर सकते हैं, जीसी पीढ़ी में विभाजित है - जनरल 0, 1 और 2, प्लस द लार्ज ऑब्जेक्ट हीप। स्प्लिट एक ढीला शब्द है - आपको मेमोरी का एक ब्लॉक मिलता है, लेकिन वहाँ पॉइंटर्स हैं जहां जनरल 0 ऑब्जेक्ट्स शुरू होते हैं और समाप्त होते हैं।

विचार प्रक्रिया यह है कि आप संभवतः बहुत सी वस्तुओं का उपयोग करेंगे जो अल्पकालिक होंगी। तो उन लोगों को जीसी के लिए आसान और तेज होना चाहिए - जनरल 0 ऑब्जेक्ट। इसलिए जब मेमोरी प्रेशर होता है, तो सबसे पहले यह एक जनरल 0 संग्रह होता है।

अब, अगर वह पर्याप्त दबाव को हल नहीं करता है, तो वह वापस चला जाता है और एक जनरल 1 स्वीप (जनरल 0 को फिर से करना) करता है, और फिर यदि पर्याप्त नहीं है, तो यह जेन 2 स्वीप करता है (जनरल 1 और जेन 0 को फिर से लोड करता है)। इसलिए लंबे समय तक जीवित वस्तुओं की सफाई में कुछ समय लग सकता है और यह महंगा हो सकता है (क्योंकि ऑपरेशन के दौरान आपके धागे निलंबित हो सकते हैं)।

इसका मतलब है कि यदि आप ऐसा कुछ करते हैं:

~MyClass() { }

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

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


8
मैं इस बात से सहमत हूं कि आप जब भी संभव हो, आईडीसोपायरी का उपयोग करना चाहते हैं, लेकिन आपके पास एक अंतिम उपकरण भी होना चाहिए जो एक डिस्पोजल विधि कहता है। आप IDispose में GC.SuppressFinalize () कॉल कर सकते हैं। अपनी डिस्पोज विधि को कॉल करने के बाद यह सुनिश्चित करने के लिए कि आपका ऑब्जेक्ट फाइनल क्यू में नहीं डाला गया है।
jColeson

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


3
"आपकी वस्तु के बारे में, कोई फर्क नहीं पड़ता कि क्या जेनरेशन 2 के लिए जीवित रहेगा।" यह बहुत बुनियादी जानकारी है! इसने एक सिस्टम के डिबगिंग के बहुत समय को बचाया, जहां अंतिम रूप देने के लिए बहुत कम समय तक चलने वाली Gen2 ऑब्जेक्ट्स "तैयार" थीं, लेकिन कभी भी भारी ढेर उपयोग के कारण आउटऑफमेरी एक्ससेप्शन को अंतिम रूप नहीं दिया गया। (यहां तक ​​कि खाली) फाइनलिज़र को हटाकर और कोड के आसपास काम करते हुए, समस्या गायब हो गई और जीसी लोड को संभालने में सक्षम हो गया।
शार्पनर

@CoryFoy "आपकी वस्तु, चाहे कोई भी हो, जेनरेशन 2 तक जीवित रहेगा" क्या इसके लिए कोई दस्तावेज है?
आशीष नेगी

33

यहां पहले से ही बहुत अच्छी चर्चा है, और मुझे पार्टी में थोड़ी देर हो गई है, लेकिन मैं खुद कुछ बिंदु जोड़ना चाहता था।

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

class SomeObject : IDisposable {
 IntPtr _SomeNativeHandle;
 FileStream _SomeFileStream;

 // Something useful here

 ~ SomeObject() {
  Dispose(false);
 }

 public void Dispose() {
  Dispose(true);
 }

 protected virtual void Dispose(bool disposing) {
  if(disposing) {
   GC.SuppressFinalize(this);
   //Because the object was explicitly disposed, there will be no need to 
   //run the finalizer.  Suppressing it reduces pressure on the GC

   //The managed reference to an IDisposable is disposed only if the 
   _SomeFileStream.Dispose();
  }

  //Regardless, clean up the native handle ourselves.  Because it is simple a member
  // of the current instance, the GC can't have done anything to it, 
  // and this is the onlyplace to safely clean up

  if(IntPtr.Zero != _SomeNativeHandle) {
   NativeMethods.CloseHandle(_SomeNativeHandle);
   _SomeNativeHandle = IntPtr.Zero;
  }
 }
}

यह सरल संस्करण है, लेकिन बहुत सारी बारीकियां हैं जो आपको इस पैटर्न पर यात्रा कर सकती हैं।

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

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

बस एक SafeHandle को परिभाषित करना यह तुच्छ बनाता है:


private class SomeSafeHandle
 : SafeHandleZeroOrMinusOneIsInvalid {
 public SomeSafeHandle()
  : base(true)
  { }

 protected override bool ReleaseHandle()
 { return NativeMethods.CloseHandle(handle); }
}

आपको निम्न प्रकार को सरल बनाने की अनुमति देता है:


class SomeObject : IDisposable {
 SomeSafeHandle _SomeSafeHandle;
 FileStream _SomeFileStream;
 // Something useful here
 public virtual void Dispose() {
  _SomeSafeHandle.Dispose();
  _SomeFileStream.Dispose();
 }
}

1
SafeHandleZeroOrMinusOneIsInvalid वर्ग कहाँ से आता है? क्या यह एक अंतर्निहित .net प्रकार है?
ओरियन एडवर्ड्स

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

1
@ ओरियनएड हां हाँ देखें msdn.microsoft.com/en-us/library/…
मार्टिन कैपोडिसीज़

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

6

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


संपादित करें: मैं दूर गया और परीक्षण किया गया, बस यह सुनिश्चित करने के लिए:

class Program
{
    static void Main(string[] args)
    {
        Fred f = new Fred();
        f = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.WriteLine("Fred's gone, and he's not coming back...");
        Console.ReadLine();
    }
}

class Fred : IDisposable
{
    ~Fred()
    {
        Console.WriteLine("Being finalized");
    }

    void IDisposable.Dispose()
    {
        Console.WriteLine("Being Disposed");
    }
}

उन वस्तुओं के बारे में धारणा बनाना जो निपटान के दौरान आपके लिए अव्यावहारिक हैं, विशेष रूप से अंतिम रूप देने के दौरान खतरनाक और मुश्किल हो सकती हैं।
स्कॉट डोरमैन

3

आपके द्वारा वर्णित मामले में नहीं, लेकिन यदि आपके पास एक है, तो GC आपके लिए अंतिम रूप से कॉल करेगा ।

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

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


1

नहीं, यह नहीं कहा जाता है।

लेकिन इससे आपकी वस्तुओं को निपटाना न भूलें। बस usingकीवर्ड का उपयोग करें ।

मैंने इसके लिए निम्नलिखित परीक्षण किया:

class Program
{
    static void Main(string[] args)
    {
        Foo foo = new Foo();
        foo = null;
        Console.WriteLine("foo is null");
        GC.Collect();
        Console.WriteLine("GC Called");
        Console.ReadLine();
    }
}

class Foo : IDisposable
{
    public void Dispose()
    {

        Console.WriteLine("Disposed!");
    }

1
यह इस बात का एक उदाहरण था कि यदि आप <code> कीवर्ड का उपयोग करके <code> का उपयोग नहीं करते हैं तो इसे कॉल नहीं किया जाएगा ... और इस स्निपेट में 9 साल, जन्मदिन मुबारक हो!
पेन्यास्किटो

1

जीसी को डिस्पोजल नहीं कहेंगे। यह आपके अंतिम रूप को कह सकता है , लेकिन यहां तक ​​कि सभी परिस्थितियों में इसकी गारंटी नहीं है।

इसे संभालने के सर्वोत्तम तरीके की चर्चा के लिए यह लेख देखें ।


0

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


0

IDisposable पैटर्न मुख्य रूप से डेवलपर द्वारा बुलाया जाने के लिए बनाया गया था, अगर आपके पास कोई ऐसी वस्तु है जो ID को लागू करता है तो डेवलपर को usingऑब्जेक्ट के संदर्भ में कीवर्ड को लागू करना चाहिए या सीधे डिस्पोज़ विधि को कॉल करना चाहिए ।

पैटर्न के लिए सुरक्षित विफल होना डिस्पोजल () पद्धति को अंतिम रूप देने वाला है। यदि आप ऐसा नहीं करते हैं, तो आप कुछ मेमोरी लीक्स बना सकते हैं: यदि आप कुछ COM आवरण बनाते हैं और कभी भी System.Runtime.Interop.Marshall.ReleaseComObject (comObject) (जिसे डिस्पोज़ विधि में रखा जाएगा) को कॉल नहीं करते हैं।

कॉल करने के लिए clr में कोई जादू नहीं है। स्वचालित रूप से ऑब्जेक्ट्स पर नज़र रखने के अलावा डिस्पोज़ करने के तरीकों के अलावा डिस्पोज़ मेथड्स और GC द्वारा फ़ाइनलाइज़र टेबल में उन्हें स्टोर करना और उन्हें कॉल करना जब कुछ क्लीन हेयुरिस्टिक्स GC द्वारा किक करते हैं।

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