मैंने https://www.spicelogic.com/Blog/net-event-handler-memory-leak-16 पर एक ब्लॉग में इस भ्रम की व्याख्या की है । मैं इसे यहाँ संक्षेप में प्रस्तुत करने का प्रयास करूँगा ताकि आपको एक स्पष्ट विचार आ सके।
संदर्भ का अर्थ है, "आवश्यकता":
सबसे पहले, आपको यह समझने की ज़रूरत है कि, यदि वस्तु A, B के संदर्भ में है, तो, इसका मतलब होगा, ऑब्जेक्ट A को कार्य करने के लिए ऑब्जेक्ट B की आवश्यकता है, है ना? इसलिए, कचरा कलेक्टर ऑब्जेक्ट B को तब तक एकत्रित नहीं करेगा जब तक कि मेमोरी में ऑब्जेक्ट A जीवित है।
मुझे लगता है कि यह हिस्सा एक डेवलपर के लिए स्पष्ट होना चाहिए।
+ = का अर्थ है, बाईं ओर की वस्तु पर राइट साइड ऑब्जेक्ट का संदर्भ इंजेक्ट करना:
लेकिन, भ्रम सी # + = ऑपरेटर से आता है। यह ऑपरेटर डेवलपर को स्पष्ट रूप से नहीं बताता है कि, इस ऑपरेटर का दाहिना हाथ वास्तव में बाईं ओर की वस्तु का संदर्भ इंजेक्ट कर रहा है।
और ऐसा करने से, ऑब्जेक्ट A सोचता है, उसे ऑब्जेक्ट B की आवश्यकता है, भले ही, आपके दृष्टिकोण से, ऑब्जेक्ट A को परवाह नहीं करनी चाहिए कि ऑब्जेक्ट B रहता है या नहीं। जैसे वस्तु A सोचती है कि वस्तु B की आवश्यकता है, वस्तु A, B को वस्तु A से बचाती है, जब तक कि वस्तु A जीवित है। लेकिन, यदि आप नहीं चाहते कि इवेंट सब्सक्राइबर ऑब्जेक्ट को दी गई सुरक्षा, तो, आप कह सकते हैं, एक मेमोरी लीक हुई।
इवेंट हैंडलर को बंद करके आप इस तरह के लीक से बच सकते हैं।
निर्णय कैसे लें?
लेकिन, आपके पूरे कोड-बेस में बहुत सारे ईवेंट और इवेंट हैंडलर हैं। क्या इसका मतलब है, आपको हर जगह इवेंट हैंडलर रखने की ज़रूरत है? इसका उत्तर है, यदि आपको ऐसा करना है, तो आपका कोडबेस क्रिया के साथ वास्तव में बदसूरत होगा।
आप यह निर्धारित करने के लिए एक साधारण प्रवाह चार्ट का पालन कर सकते हैं कि क्या एक कोचिंग इवेंट हैंडलर आवश्यक है या नहीं।
अधिकांश समय, आपको लग सकता है कि इवेंट सब्सक्राइबर ऑब्जेक्ट इवेंट पब्लिशर ऑब्जेक्ट जितना ही महत्वपूर्ण है और दोनों एक ही समय में रहने वाले हैं।
ऐसे परिदृश्य का उदाहरण जहां आपको चिंता करने की आवश्यकता नहीं है
उदाहरण के लिए, एक बटन एक खिड़की की घटना पर क्लिक करें।
यहां, इवेंट प्रकाशक बटन है, और इवेंट सब्सक्राइबर मेनविंडो है। उस फ्लो चार्ट को लागू करते हुए, एक प्रश्न पूछें, क्या मेन विंडो (ईवेंट सब्सक्राइबर) बटन (इवेंट पब्लिशर) से पहले मृत होना चाहिए? जाहिर है कि नहीं? यह भी मतलब नहीं होगा। फिर, क्लिक इवेंट हैंडलर की कोचिंग के बारे में चिंता क्यों करें?
एक उदाहरण जब एक घटना हैंडलर टुकड़ी एक जरूरी है।
मैं एक उदाहरण प्रदान करूंगा, जहां प्रकाशक ऑब्जेक्ट से पहले ग्राहक ऑब्जेक्ट मृत माना जाता है। कहते हैं, आपका MainWindow "SomeHappened" नामक एक घटना प्रकाशित करता है और आप एक बटन क्लिक करके मुख्य विंडो से एक बच्चा विंडो दिखाते हैं। चाइल्ड विंडो मुख्य विंडो के उस इवेंट को सब्सक्राइब करती है।
और, चाइल्ड विंडो मेन विंडो के एक इवेंट को सब्सक्राइब करती है।
इस कोड से, हम स्पष्ट रूप से समझ सकते हैं कि मेन विंडो में एक बटन है। उस बटन पर क्लिक करने पर एक बाल खिड़की दिखाई देती है। चाइल्ड विंडो मुख्य विंडो से एक ईवेंट सुनता है। कुछ करने के बाद, उपयोगकर्ता चाइल्ड विंडो बंद कर देता है।
अब, यदि आप एक प्रश्न पूछते हैं, तो प्रवाह चार्ट के अनुसार, क्या मैं (प्रकाशक) मुख्य विंडो से पहले बच्चे की खिड़की (इवेंट सब्सक्राइबर) मृत होना चाहिए? जवाब हां होना चाहिए। सही है? तो, इवेंट हैंडलर को अलग करें। .मैं आमतौर पर विंडो के अनलोडेड इवेंट से करता हूं।
अंगूठे का एक नियम: यदि आपका दृश्य (यानी WPF, WinForm, UWP, Xamarin फॉर्म, आदि) एक ViewModel की घटना के लिए सदस्यता लेता है, तो हमेशा घटना हैंडलर को अलग करने के लिए याद रखें। क्योंकि एक ViewModel आमतौर पर एक दृश्य से अधिक समय तक रहता है। इसलिए, अगर ViewModel को नष्ट नहीं किया गया है, तो उस ViewModel की सदस्यता वाली कोई भी घटना स्मृति में रहेगी, जो अच्छा नहीं है।
एक मेमोरी प्रोफाइलर का उपयोग करके अवधारणा का प्रमाण।
यह बहुत मजेदार नहीं होगा अगर हम एक मेमोरी प्रोफाइलर के साथ अवधारणा को मान्य नहीं कर सकते हैं। मैंने इस प्रयोग में JetBrain dotMemory प्रोफाइलर का उपयोग किया है।
सबसे पहले, मैंने मेनविंडो चलाया है, जो इस तरह दिखाता है:
फिर, मैंने एक मेमोरी स्नैपशॉट लिया। फिर मैंने 3 बार बटन क्लिक किया । तीन बाल खिड़कियों को दिखाया। मैंने उन सभी चाइल्ड विंडो को बंद कर दिया है और डॉट मेमेरी प्रोफाइलर में फोर्स जीसी बटन पर क्लिक करके यह सुनिश्चित करने के लिए कि गारबेज कलेक्टर कहा जाता है। फिर, मैंने एक और मेमोरी स्नैपशॉट लिया और इसकी तुलना की। देखो! हमारा डर सच था। बंद होने के बाद भी चाइल्ड विंडो को गारबेज कलेक्टर द्वारा एकत्र नहीं किया गया था। केवल इतना ही नहीं, बल्कि चाइल्डविंडो ऑब्जेक्ट के लिए लीक हुई ऑब्जेक्ट काउंट को भी " 3 " दिखाया गया है (मैंने 3 चाइल्ड विंडो दिखाने के लिए बटन को 3 बार क्लिक किया)।
ठीक है, फिर, मैंने इवेंट हैंडलर को अलग कर दिया जैसा कि नीचे दिखाया गया है।
फिर, मैंने उसी चरणों का प्रदर्शन किया है और मेमोरी प्रोफाइलर की जांच की है। इस बार, वाह! कोई और अधिक स्मृति रिसाव।