2008-08-18 30 views
10

मैंने अपने डब्ल्यूपीएफ एप्लिकेशन में एक "संलग्न व्यवहार" बनाया है जो मुझे एंटर कीप्रेस को संभालने और अगले नियंत्रण में जाने देता है। मैं इसे EnterKeyTraversal.Is सक्षम कहता हूं, और आप मेरे ब्लॉग here पर कोड देख सकते हैं।संलग्न व्यवहार के साथ मेमोरी लीक को रोकना

मेरी मुख्य चिंता यह है कि मेरे पास स्मृति रिसाव हो सकती है, क्योंकि मैं UIElements पर पूर्वावलोकनकेडडाउन ईवेंट को संभालने में सक्षम हूं और कभी भी घटना को "अनदेखा" नहीं करता हूं।

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

उत्तर

4

मैं DannySmurf

सहमत नहीं हूँ कुछ WPF लेआउट वस्तुओं अपनी स्मृति को अवरुद्ध करने और अपने आवेदन वास्तव में धीमी गति से कर जब वे कचरा एकत्र नहीं कर रहे हैं कर सकते हैं। तो मुझे शब्दों की पसंद सही होने का विकल्प मिलती है, आप उन ऑब्जेक्ट्स को याद कर रहे हैं जिन्हें आप अब उपयोग नहीं करते हैं। आप वस्तुओं को कचरा इकट्ठा करने की उम्मीद करते हैं, लेकिन वे नहीं हैं, क्योंकि वहां कोई संदर्भ है (इस मामले में किसी ईवेंट हैंडलर से)।

अब कोई वास्तविक जवाब :)

के लिए मैं सलाह देने के लिए आप इस WPF Performance article on MSDN

वस्तुओं पर इवेंट हैंडलर निकाल नहीं वस्तुओं जिंदा रख सकते हैं

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

वे आपको Weak Event pattern

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

आशा इस मदद करता है!

+2

मैं इसे वोट दे रहा हूं क्योंकि उत्तर के वास्तविक प्रश्न से कोई लेना देना नहीं है। आम तौर पर हर किसी को स्मृति रिसाव और उसके कारणों के गड्ढे के बारे में पता है, विशेष रूप से घटना के हैंडलरों के कारण आपने उल्लेख किया है। यहां @Matt क्या जानना चाहता था कि अंदरूनी व्यवहारों का उपयोग करते समय ईवेंट हैंडलर को सुरक्षित रूप से कैसे संभालना है। मैं जल्द ही इसका जवाब दूंगा। –

0

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

1

@ निक हाँ, संलग्न व्यवहार की बात यह है कि परिभाषा के अनुसार वे उन तत्वों के समान नहीं हैं जिनके ईवेंट आप प्रबंधित कर रहे हैं।

मुझे लगता है कि उत्तर किसी भी तरह वीक रेफरेंस का उपयोग करने के भीतर है, लेकिन मुझे यह समझाने के लिए कोई आसान कोड नमूने नहीं देखा है। :)

0

मैंने अभी आपके ब्लॉग पोस्ट को पढ़ा है और मुझे लगता है कि आपको थोड़ी भ्रामक सलाह मिली है, मैट। यदि वास्तविक मेमोरी लीक यहां है, तो यह .NET Framework में एक बग है, और ऐसा कुछ नहीं जो आप अपने कोड में आवश्यक रूप से ठीक कर सकते हैं।

मुझे लगता है कि आप (और आपके ब्लॉग पर पोस्टर) वास्तव में यहां बात कर रहे हैं वास्तव में एक रिसाव नहीं है, बल्कि स्मृति की एक सतत खपत है। यह वही बात नहीं है। स्पष्ट होने के लिए, लीक मेमोरी स्मृति है जो किसी प्रोग्राम द्वारा आरक्षित होती है, फिर त्याग दिया जाता है (यानी, एक सूचक लटकता रहता है), और बाद में इसे मुक्त नहीं किया जा सकता है। चूंकि स्मृति .NET में प्रबंधित है, यह सैद्धांतिक रूप से असंभव है। हालांकि, यह संभव है कि एक कार्यक्रम के लिए गुंजाइश से बाहर जाने के लिए संदर्भों की अनुमति के बिना स्मृति की लगातार बढ़ती मात्रा को आरक्षित करने के लिए (और कचरा संग्रह के लिए योग्य बनें); हालांकि स्मृति को लीक नहीं किया गया है। एक बार आपका प्रोग्राम निकलने के बाद जीसी इसे सिस्टम में वापस कर देगा।

तो। अपने प्रश्न का उत्तर देने के लिए, मुझे नहीं लगता कि आपको वास्तव में यहां कोई समस्या है। आपके पास निश्चित रूप से मेमोरी रिसाव नहीं है, और आपके कोड से, मुझे नहीं लगता कि आपको चिंता करने की ज़रूरत है, जहां तक ​​स्मृति खपत भी जाती है। जब तक आप यह सुनिश्चित न करें कि आप बार-बार उस ईवेंट हैंडलर को कभी भी इसे निर्दिष्ट किए बिना असाइन नहीं कर रहे हैं (यानी, कि आप या तो इसे कभी भी एक बार सेट करते हैं, या आप इसे निर्दिष्ट करते समय प्रत्येक बार इसे हटा देते हैं), जो ऐसा लगता है कि आपका कोड ठीक होना चाहिए।

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

0

@Arcturus:

... अपनी स्मृति को अवरुद्ध और वास्तव में धीमी गति से जब वे कचरा एकत्र नहीं कर रहे हैं अपने आवेदन कर सकते हैं।

यह स्पष्ट रूप से स्पष्ट है, और मैं असहमत नहीं हूं। हालांकि:

... आप लीक कर रहे हैं स्मृति है कि आप अब का उपयोग करें ... क्योंकि उन्हें लिए एक संदर्भ है वहाँ आपत्ति उठाने का।

(विकिपीडिया, "मेमोरी रिसाव")

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

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

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

+0

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

4

हाँ मुझे पता है कि पुराने दिनों में मेमोरी लीक एक पूरी तरह से अलग विषय है।

क्यों WeakEvent पैटर्न को लागू करें: लेकिन प्रबंधित कोड, अवधि स्मृति रिसाव अधिक उपयुक्त हो सकता है के लिए नए अर्थ के साथ ...

माइक्रोसॉफ्ट भी मानता है यह एक स्मृति रिसाव होने के लिए?

घटनाओं के लिए सुनना मेमोरी लीक का कारण बन सकता है। किसी ईवेंट को सुनने के लिए विशिष्ट तकनीक भाषा-विशिष्ट वाक्यविन्यास का उपयोग करना है जो स्रोत पर किसी ईवेंट के लिए हैंडलर को जोड़ता है। उदाहरण के लिए, सी # में, वाक्यविन्यास है: स्रोत। SomEvent + = नया कुछ EventHandler (MyEventHandler)।

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

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

इसके अलावा, मुझे अपने प्रबंधक को यह बताने में बहुत आसान लगता है कि हमारे पास स्मृति की रिसाव है, उसे समझाते हुए कि कुछ वस्तुओं को सफाई की आवश्यकता वाले कुछ घटनाओं के कारण कचरा नहीं है;)

-1

ठीक है कि (प्रबंधक बिट) मैं निश्चित रूप से समझ सकता हूं, और सहानुभूति व्यक्त कर सकता हूं।

लेकिन जो भी माइक्रोसॉफ्ट इसे कॉल करता है, मुझे नहीं लगता कि एक "नई" परिभाषा उचित है।यह जटिल है, क्योंकि हम 100% प्रबंधित दुनिया में नहीं रहते हैं (भले ही माइक्रोसॉफ्ट यह दिखाता है कि हम ऐसा करते हैं, माइक्रोसॉफ्ट स्वयं ऐसी दुनिया में नहीं रहता है)। जब आप मेमोरी रिसाव कहते हैं, तो आप इसका मतलब यह कर सकते हैं कि एक प्रोग्राम बहुत अधिक स्मृति का उपभोग कर रहा है (यह उपयोगकर्ता की परिभाषा है), या एक प्रबंधित संदर्भ बाहर निकलने तक (यहां तक ​​कि) मुक्त नहीं किया जाएगा, या एक अप्रबंधित संदर्भ ठीक से साफ नहीं किया जा रहा है अप (वह एक वास्तविक स्मृति रिसाव होगा), या प्रबंधित कोड से बुलाया गया अप्रबंधित कोड स्मृति (एक और वास्तविक रिसाव) लीक कर रहा है।

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

वैसे भी। इसका मतलब भाषा के बारे में एक हवादार परी चर्चा में बदलने का मतलब नहीं है। बस कह ...

0

यह सच है सच है,

आप सही निश्चित रूप से कर रहे हैं .. लेकिन वहाँ प्रोग्रामर की एक पूरी नई पीढ़ी इस दुनिया कि स्पर्श कभी नहीं होगा अप्रबंधित कोड में पैदा किया जा रहा है, और मैं भाषा परिभाषाओं का मानना ​​है कि ऐसा करने फिर से बार-बार आविष्कार करेगा। डब्ल्यूपीएफ में मेमोरी लीक सी/सीपीपी कहने से अलग है।

या मेरे प्रबंधकों के लिए पाठ्यक्रम मैंने इसे स्मृति रिसाव के रूप में संदर्भित किया .. मेरे साथी सहयोगियों के लिए मैंने इसे एक प्रदर्शन मुद्दे के रूप में संदर्भित किया!

मैट की समस्या का जिक्र करते हुए, यह एक प्रदर्शन समस्या हो सकती है जिसे आपको निपटने की आवश्यकता हो सकती है। यदि आप बस कुछ स्क्रीन का उपयोग करते हैं और आप उन स्क्रीन नियंत्रण सिंगलेट्स बनाते हैं, तो आपको शायद यह समस्या दिखाई न दे;)।

ue.PreviewKeyDown += ue_PreviewKeyDown; 

ue_PreviewKeyDown के लिए एक कठिन संदर्भ ue.PreviewKeyDown में संग्रहीत है:

3

दार्शनिक बहस अलग रूप में, ओ पी के ब्लॉग पोस्ट को देख में, मैं यहाँ किसी भी रिसाव दिख रहा है।

ue_PreviewKeyDownSTATIC विधि है और GCed नहीं हो सकता है।

ue के लिए कोई कठोर संदर्भ संग्रहीत नहीं किया जा रहा है, इसलिए GCed होने से कुछ भी इसे रोक नहीं रहा है।

तो ... रिसाव कहां है?

+2

यह एक आम गलतफहमी है। ue.PreviewKeyDown + = ue_PreviewKeyDown ue के लिए एक मजबूत संदर्भ रखता है और क्योंकि us_PreviewKeyDown स्थिर है और ue कभी एकत्र नहीं किया जाएगा। – SACO

+0

@ एसएसीओ क्या आप इसे समझा सकते हैं? रखा जा रहा है "ue के लिए मजबूत संदर्भ" कहां रखा जा रहा है? जहां तक ​​मैं देख सकता हूं, जॉन पूरी तरह से सही है, मूल उदाहरण में बिल्कुल कोई स्मृति रिसाव नहीं है। 'ue.PreviewKeyDown - = ue_PreviewKeyDown' आवश्यक नहीं है। – Golvellius

+0

@ गोल्वेलियस मैंने एक उत्तर पोस्ट किया जो मेरे बिंदु को समझाएगा। मैंने वास्तव में इसका परीक्षण किया और पता चला कि ue_PreviewKeyDown स्थिर है तो कोई रिसाव नहीं होगा। – SACO

1

जॉन फेंटन पोस्ट पर मेरी टिप्पणी को समझाने के लिए यहां मेरा जवाब है।

class Program 
{ 
    static void Main(string[] args) 
    { 
     var a = new A(); 
     var b = new B(); 

     a.Clicked += b.HandleClicked; 
     //a.Clicked += B.StaticHandleClicked; 
     //A.StaticClicked += b.HandleClicked; 

     var weakA = new WeakReference(a); 
     var weakB = new WeakReference(b); 

     a = null; 
     //b = null; 

     GC.Collect(); 
     GC.WaitForPendingFinalizers(); 
     GC.Collect(); 

     Console.WriteLine("a is alive: " + weakA.IsAlive); 
     Console.WriteLine("b is alive: " + weakB.IsAlive); 
     Console.ReadKey(); 
    } 


} 

class A 
{ 
    public event EventHandler Clicked; 
    public static event EventHandler StaticClicked; 
} 

class B 
{ 
    public void HandleClicked(object sender, EventArgs e) 
    { 
    } 

    public static void StaticHandleClicked(object sender, EventArgs e) 
    { 
    } 
} 

आप

a.Clicked += b.HandleClicked; 

है और केवल ख अशक्त दोनों संदर्भों को weakA करने के लिए सेट और जिंदा रहने के weakB हैं: निम्न उदाहरण देखते हैं! यदि आप केवल एक नल बी सेट करते हैं तो जिंदा रहता है लेकिन एक नहीं (जो साबित करता है कि जॉन फेंटन गलत कह रहा है कि घटना प्रदाता में एक कठिन संदर्भ संग्रहीत किया जाता है - इस मामले में ए)।

यह मैं गलत निष्कर्ष करने के लिए नेतृत्व कि

a.Clicked += B.StaticHandleClicked; 

एक रिसाव का कारण बन क्योंकि मैं हालांकि एक के कहने स्थिर हैंडलर द्वारा रखा जाएगा। यह मामला नहीं है (मेरे कार्यक्रम का परीक्षण करें)।स्थैतिक घटना हैंडलर या घटनाओं के मामले में यह दूसरी तरफ है। आप लिखते हैं तो

A.StaticClicked += b.HandleClicked; 

एक संदर्भ ख के लिए रखा जाएगा।

+0

मेरी टिप्पणी का जवाब देने का समय निवेश करने के लिए धन्यवाद, लेकिन: जॉन फेंटन गलत नहीं है कि घटना प्रदाता में एक कठिन संदर्भ संग्रहीत किया जाता है। 'ए' को शून्य पर सेट करके, आप मूल रूप से "हार्ड" संदर्भ को हटा रहे हैं क्योंकि मेमोरी ऑब्जेक्ट' ए' पॉइंट्स बाद में कचरा इकट्ठा किया जाता है। और 'ए। क्लाक्टेड + = बीस्टैटहैंडलेक्लेक्ड;' ओपी की स्थिति बिल्कुल ठीक है - यह कभी भी स्मृति रिसाव का कारण नहीं बन सकती है, इसलिए जॉन फेंटन का सवाल _So ... रिसाव कहां है? _। जैसा कि आपने बताया, केवल दूसरी तरफ दौर, लेकिन ओपी की स्थिति ऐसी स्थिति नहीं है। – Golvellius