डिस्पोजल का बिंदु मानव रहित संसाधनों को मुक्त करना है। इसे किसी बिंदु पर करने की आवश्यकता है, अन्यथा वे कभी भी साफ नहीं किए जाएंगे। कचरा संग्रहकर्ता को पता नहीं है कि किसDeleteHandle()
प्रकार के चर पर कॉल करना है IntPtr
, यह नहीं जानता कि उसे कॉल करने की आवश्यकता है या नहीं DeleteHandle()
।
नोट : एक अप्रबंधित संसाधन क्या है ? यदि आपने इसे Microsoft .NET फ्रेमवर्क में पाया है: यह प्रबंधित है। यदि आप अपने आप MSDN के आसपास poking गए, तो यह अप्रबंधित है। .NET फ्रेमवर्क में आपके लिए उपलब्ध हर चीज़ की अच्छी कम्फर्टेबल दुनिया से बाहर निकलने के लिए आपने P / Invoke कॉल्स का उपयोग किया है।
आपके द्वारा बनाई गई वस्तु को कुछ विधि को उजागर करने की आवश्यकता है , जिसे बाहरी दुनिया कॉल कर सकती है, ताकि मानव रहित संसाधनों को साफ किया जा सके। विधि को जो भी आप पसंद करते हैं उसका नाम दिया जा सकता है:
public void Cleanup()
या
public void Shutdown()
लेकिन इसके बजाय इस विधि के लिए एक मानकीकृत नाम है:
public void Dispose()
यहां तक कि एक इंटरफ़ेस भी बनाया गया था IDisposable
, जिसमें बस एक तरीका है:
public interface IDisposable
{
void Dispose()
}
इसलिए आप अपनी वस्तु को IDisposable
इंटरफ़ेस से बाहर कर देते हैं, और इस तरह से आप वादा करते हैं कि आपने अपने अप्रबंधित संसाधनों को साफ करने के लिए उस एकल विधि को लिखा है:
public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
और आपने कल लिया। सिवाय आप बेहतर कर सकते हैं।
क्या होगा यदि आपकी वस्तु ने 250MB System.Drawing.Bitmap (यानी .NET प्रबंधित बिटमैप वर्ग) को किसी प्रकार के फ्रेम बफर के रूप में आवंटित किया है ? ज़रूर, यह एक प्रबंधित .NET ऑब्जेक्ट है, और कचरा संग्रहकर्ता इसे मुक्त करेगा। लेकिन क्या आप वास्तव में 250 एमबी मेमोरी छोड़ना चाहते हैं, बस वहां बैठे - कचरा इकट्ठा करने वाले के लिए आखिरकार साथ आना और उसे मुक्त करना है? क्या होगा अगर एक खुला डेटाबेस कनेक्शन है ? निश्चित रूप से हम नहीं चाहते हैं कि कनेक्शन खुला रहे, वस्तु को अंतिम रूप देने के लिए जीसी का इंतजार करना चाहिए।
यदि उपयोगकर्ता ने कॉल किया है Dispose()
(मतलब वे अब ऑब्जेक्ट का उपयोग करने की योजना नहीं बनाते हैं) तो उन बेकार बिटमैप और डेटाबेस कनेक्शन से छुटकारा क्यों नहीं मिलता है?
तो अब हम करेंगे:
- अप्रबंधित संसाधनों से छुटकारा पाएं (क्योंकि हमें), और
- प्रबंधित संसाधनों से छुटकारा पाएं (क्योंकि हम सहायक बनना चाहते हैं)
तो आइए Dispose()
उन प्रबंधित वस्तुओं से छुटकारा पाने के लिए हमारी विधि को अपडेट करें:
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
और सब अच्छा है, इसके अलावा आप बेहतर कर सकते हैं !
क्या होगा यदि व्यक्ति आपकी वस्तु पर कॉल करना भूल गयाDispose()
? तब वे कुछ अप्रबंधित संसाधनों को लीक कर देते थे !
नोट: वे प्रबंधित संसाधनों को लीक नहीं करेंगे , क्योंकि अंततः कचरा कलेक्टर एक पृष्ठभूमि थ्रेड पर, और किसी भी अप्रयुक्त ऑब्जेक्ट से जुड़ी मेमोरी को चलाने के लिए जा रहा है। यह आपके वस्तु, और किसी भी प्रबंधित वस्तुओं आप का उपयोग (जैसे शामिल होंगे Bitmap
और DbConnection
)।
यदि व्यक्ति कॉल करना भूल गया Dispose()
, तो हम अभी भी अपने बेकन को बचा सकते हैं ! हमारे पास अभी भी उनके लिए इसे कॉल करने का एक तरीका है : जब कचरा कलेक्टर आखिरकार हमारी वस्तु को मुक्त करने (यानी अंतिम रूप देने) के लिए चारों ओर हो जाता है।
नोट: कचरा कलेक्टर अंततः सभी प्रबंधित वस्तुओं को मुक्त कर देगा। जब यह होता है, तो यह Finalize
ऑब्जेक्ट पर विधि को कॉल करता है । जीसी आपके डिस्पोजल विधि के बारे में नहीं जानता, या देखभाल नहीं करता है । यह सिर्फ एक नाम था जिसे हमने एक विधि के लिए चुना था जिसे हम कॉल करते हैं जब हम अप्रबंधित सामान से छुटकारा चाहते हैं।
गारबेज कलेक्टर द्वारा हमारी वस्तु का विनाश उन pesky मानव रहित संसाधनों को मुक्त करने का सही समय है। हम इस Finalize()
विधि को ओवरराइड करके करते हैं।
नोट: C # में, आप स्पष्ट रूप से Finalize()
विधि को ओवरराइड नहीं करते हैं । आप एक ऐसी विधि लिखते हैं जो C ++ विध्वंसक की तरह दिखती है , और संकलक उस Finalize()
तरीके को अपनाता है:
~MyObject()
{
//we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
Dispose(); //<--Warning: subtle bug! Keep reading!
}
लेकिन उस कोड में एक बग है। आप देखते हैं, कचरा कलेक्टर एक पृष्ठभूमि धागे पर चलता है ; आप उस क्रम को नहीं जानते हैं जिसमें दो वस्तुएं नष्ट हो जाती हैं। यह पूरी तरह से संभव है कि आपके Dispose()
कोड में, जिस प्रबंधित वस्तु से आप छुटकारा पाने की कोशिश कर रहे हैं (क्योंकि आप मददगार बनना चाहते थे) अब नहीं है:
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
this.frameBufferImage = null;
}
}
तो आपको जो कुछ भी चाहिए वह यह Finalize()
बताने का एक तरीका है Dispose()
कि इसे किसी भी प्रबंधित संसाधनों को नहीं छूना चाहिए (क्योंकि वे अब वहां नहीं हो सकते हैं), जबकि अभी भी मानव रहित संसाधनों को मुक्त करना है।
ऐसा करने के लिए मानक पैटर्न है Finalize()
और Dispose()
दोनों एक तिहाई (!) विधि कहते हैं; यदि आप यह कहते हुए बुलियन पास करते हैं कि आप इसे Dispose()
(विरोध के अनुसार Finalize()
) कह रहे हैं , तो इसका मतलब है कि प्रबंधित संसाधनों को मुक्त करना सुरक्षित है।
इस आंतरिक विधि को "CoreDispose", या "MyInternalDispose" जैसे कुछ मनमाने नाम दिए जा सकते हैं, लेकिन इसे कॉल करने की परंपरा है Dispose(Boolean)
:
protected void Dispose(Boolean disposing)
लेकिन एक अधिक उपयोगी पैरामीटर नाम हो सकता है:
protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too, but only if I'm being called from Dispose
//(If I'm being called from Finalize then the objects might not exist
//anymore
if (itIsSafeToAlsoFreeManagedObjects)
{
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
}
और आप अपनी IDisposable.Dispose()
विधि के कार्यान्वयन को इसमें बदल देते हैं:
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
}
और आपका अंतिम रूप:
~MyObject()
{
Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}
नोट : यदि आपकी वस्तु किसी ऐसी वस्तु से उतरती है Dispose
, जो लागू होती है , तो जब आप ओवरराइड करें तो उनकी बेस डिस्पोज विधि को कॉल करना न भूलें :
public override void Dispose()
{
try
{
Dispose(true); //true: safe to free managed resources
}
finally
{
base.Dispose();
}
}
और सब अच्छा है, इसके अलावा आप बेहतर कर सकते हैं !
यदि उपयोगकर्ता Dispose()
आपकी वस्तु पर कॉल करता है, तो सब कुछ साफ हो गया है। बाद में, जब कचरा कलेक्टर के साथ आता है और अंतिम रूप से कॉल करता है, तो यह फिर से कॉल करेगा Dispose
।
न केवल यह बेकार है, लेकिन अगर आपकी वस्तु में पिछली कॉल से पहले से निपटाई गई वस्तुओं के संदर्भ हैं Dispose()
, तो आप उन्हें फिर से निपटाने की कोशिश करेंगे!
आप अपने कोड में देखेंगे कि मैं उन वस्तुओं के संदर्भों को हटाने के लिए सावधान था, जिन्हें मैंने निपटाया है, इसलिए मैं Dispose
एक जंक ऑब्जेक्ट संदर्भ पर कॉल करने का प्रयास नहीं करता । लेकिन उस में रेंगने से एक सूक्ष्म बग को नहीं रोका।
जब उपयोगकर्ता कॉल करता है Dispose()
: हैंडल CursorFileBitmapIconServiceHandle नष्ट हो जाता है। बाद में जब कचरा कलेक्टर चलता है, तो वह उसी हैंडल को फिर से नष्ट करने की कोशिश करेगा।
protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy
...
}
जिस तरह से आप इसे ठीक करते हैं वह कचरा कलेक्टर को बताता है कि उसे ऑब्जेक्ट को अंतिम रूप देने में परेशान होने की आवश्यकता नहीं है - इसके संसाधनों को पहले ही साफ कर दिया गया है, और अधिक काम की आवश्यकता नहीं है। आप विधि GC.SuppressFinalize()
में कॉल करके ऐसा करते हैं Dispose()
:
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}
अब जब कि उपयोगकर्ता ने कॉल किया है Dispose()
, हमारे पास है:
- मानव रहित संसाधनों को मुक्त किया
- संसाधनों का प्रबंधन किया
जीसी में अंतिम बिंदु चलाने वाले का कोई मतलब नहीं है - सब कुछ का ध्यान रखा गया है।
क्या मैं अप्रबंधित संसाधनों की सफाई के लिए अंतिम रूप का उपयोग नहीं कर सकता हूं?
के लिए प्रलेखन Object.Finalize
कहता है:
अंतिम विधि का उपयोग वस्तु को नष्ट करने से पहले वर्तमान वस्तु द्वारा आयोजित मानव रहित संसाधनों पर सफाई संचालन करने के लिए किया जाता है।
लेकिन MSDN प्रलेखन यह भी कहता है IDisposable.Dispose
:
अप्रबंधित संसाधनों को मुक्त करने, जारी करने या रीसेट करने से जुड़े एप्लिकेशन-परिभाषित कार्य करता है।
तो यह कौन सा है? मानव रहित संसाधनों की सफाई के लिए मेरे लिए कौन सा स्थान है? उत्तर है:
यह तुम्हारी पसंद है! लेकिन चुनें Dispose
।
आप निश्चित रूप से अपने अप्रबंधित सफाई को अंतिम रूप दे सकते हैं:
~MyObject()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//A C# destructor automatically calls the destructor of its base class.
}
उस समस्या से आपको कोई मतलब नहीं है जब कचरा इकट्ठा करने वाला आपकी वस्तु को अंतिम रूप देने में जुट जाएगा। आपके संयुक्त राष्ट्र के प्रबंधित, गैर-आवश्यक, संयुक्त राष्ट्र के उपयोग किए गए मूल संसाधन तब तक चिपकेंगे जब तक कि कचरा कलेक्टर अंततः नहीं चलता। तब यह आपकी अंतिम विधि कहेगा; मानव रहित संसाधनों की सफाई। Object.Finalize के प्रलेखन यह बताते हैं:
अंतिम समय जब अंजाम होता है अपरिभाषित। अपनी कक्षा के उदाहरणों के लिए संसाधनों के निर्धारक रिलीज को सुनिश्चित करने के लिए, एक करीबी तरीका लागू करें या एक IDisposable.Dispose
कार्यान्वयन प्रदान करें ।
यह Dispose
मानव रहित संसाधनों को साफ करने के लिए उपयोग करने का गुण है ; आपको पता है, और नियंत्रण, जब अप्रबंधित संसाधन साफ कर रहे हैं। उनका विनाश "नियतात्मक" है ।
अपने मूल प्रश्न का उत्तर देने के लिए: जीसी को करने का निर्णय लेने के बजाय, अब मेमोरी को क्यों न जारी करें? मेरे पास एक फेशियल रिकॉग्निशन सॉफ्टवेयर है जो 530 एमबी आंतरिक छवियों से छुटकारा पाने की आवश्यकता है , क्योंकि अब उनकी कोई आवश्यकता नहीं है। जब हम नहीं करते हैं: मशीन एक स्वैपिंग पड़ाव के लिए पीसती है।
बोनस पढ़ना
जो कोई भी इस सवाल का जवाब की शैली (समझा पसंद करती है के लिए क्यों है, तो कैसे स्पष्ट हो जाता है), मैं सुझाव है कि आप डॉन बॉक्स की असेंशियल कॉम के अध्याय एक पढ़ें:
35 पृष्ठों में वह द्विआधारी वस्तुओं का उपयोग करने की समस्याओं की व्याख्या करता है, और आपकी आंखों के सामने COM को लागू करता है। एक बार जब आप का एहसास क्यों कॉम की, शेष 300 पृष्ठों स्पष्ट हैं, और सिर्फ विस्तार माइक्रोसॉफ्ट के कार्यान्वयन।
मुझे लगता है कि हर प्रोग्रामर जो कभी वस्तुओं या कॉम के साथ निपटा है, बहुत कम से कम, पहले अध्याय को पढ़ना चाहिए। यह किसी भी चीज़ का सबसे अच्छा विवरण है।
अतिरिक्त बोनस पढ़ना
जब आप जो कुछ भी जानते हैं वह एरिक लिपर्ट द्वारा गलत है
इसलिए वास्तव में एक सही फाइनल लिखना बहुत मुश्किल है, और जो सबसे अच्छी सलाह मैं आपको दे सकता हूं वह है कोशिश न करना ।