प्रस्तावना
निम्नलिखित कोड पर विचार करें:
मार्शाल का हस्ताक्षर।
FinalReleaseComObject विधि इस प्रकार है:
public static int FinalReleaseComObject(Object o)
हम एक साधारण COM ऑब्जेक्ट बनाते हैं, कुछ काम करते हैं, और तुरंत इसे जारी करते हैं। ऐसा लगता है कि क्या गलत हो सकता है? हां, अनंत लूप के अंदर एक वस्तु का निर्माण एक अच्छा अभ्यास नहीं है, लेकिन
जीसी सभी गंदे काम करेगा। वास्तविकता थोड़ी अलग है:

यह समझने के लिए कि स्मृति लीक क्यों है, आपको यह समझने की आवश्यकता है कि
गतिशील कैसे काम करता है। इस विषय पर पहले से ही हैबे पर कई लेख हैं, उदाहरण के लिए
यह एक है , लेकिन वे कार्यान्वयन के विवरण में नहीं जाते हैं, इसलिए हम अपने स्वयं के अनुसंधान का संचालन करेंगे।
सबसे पहले, हम
गतिशील कामकाजी तंत्र के बारे में विस्तार से जांच करेंगे, फिर हम एक चित्र में अर्जित ज्ञान को कम कर देंगे और अंत में हम इस रिसाव के कारणों और इससे बचने के तरीके के बारे में चर्चा करेंगे। कोड में गोता लगाने से पहले, चलो स्रोत डेटा को स्पष्ट करते हैं: कारकों का कौन सा संयोजन रिसाव की ओर जाता है?
प्रयोगों
शायद कई
देशी COM ऑब्जेक्ट बनाना अपने आप में एक बुरा विचार है? आइए देखें:
इस बार सब कुछ अच्छा है:

आइए कोड के मूल संस्करण पर वापस जाएं, लेकिन ऑब्जेक्ट का प्रकार बदलें:
और फिर, कोई आश्चर्य नहीं:

तीसरा विकल्प आजमाते हैं:
खैर अब, हमें निश्चित रूप से एक ही व्यवहार करना चाहिए! हाँ? नहीं :(

एक समान चित्र होगा यदि आप कॉम को एक
वस्तु के रूप में घोषित करते हैं या यदि आप
प्रबंधित COM के साथ काम करते हैं। प्रयोगात्मक परिणामों को संक्षेप में लिखें:
- देशी COM ऑब्जेक्ट्स को अपने आप से इंस्टेंट करने से लीक नहीं होता - GC सफलतापूर्वक मेमोरी को साफ़ करने में सफल होता है
- किसी भी प्रबंधित वर्ग के साथ काम करते समय, लीक नहीं होते हैं
- जब स्पष्ट रूप से किसी ऑब्जेक्ट को ऑब्जेक्ट पर कास्टिंग किया जाता है, तो सब कुछ ठीक है
आगे देखते हुए, पहले बिंदु पर हम इस तथ्य को जोड़ सकते हैं कि
गतिशील वस्तुओं के साथ काम करना (तरीकों को कॉल करना या गुणों के साथ काम करना) भी लीक नहीं करता है। यह निष्कर्ष स्वयं ही बताता है: स्मृति रिसाव तब होता है जब हम एक
गतिशील पैरामीटर (बिना "मैनुअल" प्रकार के रूपांतरण) को
देशी कॉम से युक्त करते हैं, एक विधि पैरामीटर के रूप में।
हमें गहराई तक जाने की जरूरत है
यह याद रखने का समय है
कि यह
गतिशील क्या है :
त्वरित संदर्भC # 4.0 एक नए प्रकार का गतिशील प्रदान करता है। यह प्रकार संकलक द्वारा स्थैतिक प्रकार की जाँच से बचा जाता है। ज्यादातर मामलों में, यह ऑब्जेक्ट प्रकार के रूप में काम करता है। संकलित समय में, यह माना जाता है कि घोषित एक तत्व किसी भी ऑपरेशन का समर्थन करता है। इसका अर्थ है कि आपको इस बारे में सोचने की ज़रूरत नहीं है कि ऑब्जेक्ट कहां से आता है - COM API से, एक गतिशील भाषा जैसे IronPython, प्रतिबिंब का उपयोग करके, या कहीं और से। इसके अलावा, यदि कोड अमान्य है, तो त्रुटियों को रनटाइम में फेंक दिया जाएगा।
उदाहरण के लिए, यदि निम्न कोड में exampleMethod1 विधि में ठीक एक पैरामीटर है, तो संकलक पहचानता है कि ec.exampleMethod1 (10, 4) विधि में पहला कॉल अमान्य है क्योंकि इसमें दो पैरामीटर हैं। यह एक संकलन त्रुटि के परिणामस्वरूप होगा। दूसरी विधि कॉल, डायनेमिक_ec.exampleMethod1 (10, 4) कंपाइलर द्वारा जाँच नहीं की जाती है, चूंकि डायनेमिक_क को डायनेमिक घोषित किया गया है। कोई संकलन त्रुटियाँ नहीं होंगी। फिर भी, त्रुटि पर किसी का ध्यान नहीं जाएगा - यह रनटाइम में पता लगाया जाएगा।
static void Main(string[] args) { ExampleClass ec = new ExampleClass();
class ExampleClass { public ExampleClass() { } public ExampleClass(int v) { } public void exampleMethod1(int i) { } public void exampleMethod2(string str) { } }
कोड जो
गतिशील चर का उपयोग करता है, संकलन के दौरान महत्वपूर्ण परिवर्तन करता है। यह कोड:
dynamic com = Activator.CreateInstance(comType); Marshal.FinalReleaseComObject(com);
निम्नलिखित में बदल जाता है:
object instance = Activator.CreateInstance(typeFromClsid);
जहाँ
o__0 उत्पन्न स्थिर वर्ग है, और
p__0 इसमें स्थिर क्षेत्र है:
private class o__0 { public static CallSite<Action<CallSite, Type, object>> p__0; }
नोट: डायनेमिक के साथ प्रत्येक इंटरैक्शन के लिए, एक कॉलसाइट क्षेत्र बनाया जाता है। यह, जैसा कि बाद में देखा जाएगा, प्रदर्शन को अनुकूलित करने के लिए आवश्यक है।ध्यान दें कि
गतिशील का कोई उल्लेख नहीं बचा है - हमारी वस्तु अब प्रकार की
वस्तु के एक चर में संग्रहीत है। आइए जनरेट कोड के माध्यम से चलते हैं। सबसे पहले, एक बंधन बनाया जाता है, जो बताता है कि हम क्या कर रहे हैं:
Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "FinalReleaseComObject", (IEnumerable<Type>) null, typeof (Foo), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[2] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, (string) null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null) })
यह हमारे गतिशील संचालन का विवरण है। आपको याद दिला दूं कि हम एक
डायनामिक वैरिएबल को
फाइनलReleaseComObject विधि से पास करते हैं।
- CSharpBinderFlags.ResultDiscarded - विधि निष्पादन का परिणाम भविष्य में उपयोग नहीं किया जाता है
- "FinalReleaseComObject" - नामक विधि का नाम
- टाइपोफ़ (फू) - ऑपरेशन संदर्भ; कॉल का प्रकार
CSharpArgumentInfo - बाध्यकारी मापदंडों का वर्णन। हमारे मामले में:
- CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, (string) null) - पहले पैरामीटर का वर्णन - मार्शल क्लास: यह स्थिर है और इसके प्रकार को बाइंडिंग माना जाना चाहिए।
- CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, (string) null) - विधि पैरामीटर का विवरण, आमतौर पर कोई अतिरिक्त जानकारी नहीं होती है।
यदि यह एक विधि को कॉल करने का सवाल नहीं था, लेकिन उदाहरण के लिए, एक
गतिशील वस्तु से एक संपत्ति को कॉल करना, तो केवल एक
CSharpArgumentInfo होगा जो स्वयं
गतिशील वस्तु का वर्णन करता है।
CallSite एक गतिशील अभिव्यक्ति पर एक आवरण है। इसमें हमारे लिए दो महत्वपूर्ण क्षेत्र शामिल हैं:
- सार्वजनिक टी अपडेट
- सार्वजनिक T लक्ष्य
उत्पन्न कोड से, यह स्पष्ट है कि जब कोई भी ऑपरेशन किया जाता है, तो
लक्ष्य का वर्णन मापदंडों के साथ किया जाता है:
Foo.o__0.p__0.Target((CallSite) Foo.o__0.p__0, typeof (Marshal), instance);
ऊपर वर्णित
CSharpArgumentInfo के साथ संयोजन में
, इस कोड का अर्थ है निम्नलिखित: आपको उदाहरण के पैरामीटर के साथ स्थिर मार्शल क्लास पर FinalReleaseComObject विधि को कॉल करने की आवश्यकता है। पहली कॉल के समय, एक ही प्रतिनिधि को
लक्ष्य में
अद्यतन के रूप में संग्रहीत किया जाता है।
अद्यतन प्रतिनिधि दो महत्वपूर्ण कार्यों के लिए जिम्मेदार है:
- एक डायनेमिक ऑपरेशन को एक स्थिर (बाइंडिंग मेकेनिज्म इस आर्टिकल के दायरे से परे है) से बांधना
- कैश फॉर्मेशन
हम दूसरे बिंदु में रुचि रखते हैं। यहां यह ध्यान दिया जाना चाहिए कि जब एक गतिशील वस्तु के साथ काम करते हैं, तो हमें हर बार ऑपरेशन की वैधता की जांच करने की आवश्यकता होती है। यह एक बजाय संसाधन-गहन कार्य है, इसलिए मैं ऐसे चेक के परिणामों को कैश करना चाहता हूं। एक पैरामीटर के साथ एक विधि को कॉल करने के संबंध में, हमें निम्नलिखित को याद रखने की आवश्यकता है:
- वह प्रकार जिस पर विधि कहलाती है
- पैरामीटर द्वारा पारित ऑब्जेक्ट का प्रकार (यह सुनिश्चित करने के लिए कि यह पैरामीटर के प्रकार पर डाला जा सकता है)
- क्या ऑपरेशन वैध है
फिर,
टारगेट को फिर से कॉल करते समय, हमें अपेक्षाकृत महंगी बाइंडिंग करने की आवश्यकता नहीं है: बस प्रकारों की तुलना करें और, यदि वे मेल खाते हैं, तो उद्देश्य फ़ंक्शन को कॉल करें। इस समस्या को हल करने के लिए, प्रत्येक डायनेमिक ऑपरेशन के लिए एक
एक्सप्रेशनट्री बनाई जाती है, जो
बाधाओं और
उद्देश्य फ़ंक्शन को संग्रहीत करती है जिससे डायनेमिक एक्सप्रेशन बंधे थे।
यह फ़ंक्शन दो प्रकार के हो सकते हैं:
- बाइंडिंग त्रुटि : उदाहरण के लिए, एक ऐसी विधि को एक गतिशील ऑब्जेक्ट पर कहा जाता है जो मौजूद नहीं है या एक डायनामिक ऑब्जेक्ट को उस पैरामीटर के प्रकार में परिवर्तित नहीं किया जा सकता है जिसमें इसे पारित किया गया है: फिर आपको Microsoft.CSharp की तरह अपवाद को फेंकने की जरूरत है । क्रमबद्धता: 'NoSuchMember'
- चुनौती कानूनी है: फिर बस आवश्यक कार्रवाई करें
यह
एक्सप्रेशनट्री अद्यतन प्रतिनिधि के निष्पादन के दौरान बनाई गई है और
लक्ष्य में संग्रहीत है।
लक्ष्य -
L0 कैश, हम बाद में कैश के बारे में अधिक बात करेंगे।
तो,
लक्ष्य अंतिम प्रतिनिधि
अभिव्यक्ति को
अद्यतन प्रतिनिधि के माध्यम से उत्पन्न करता है। आइए देखें कि यह
नियम बू विधि में पारित एक
प्रबंधित प्रकार का उदाहरण कैसे दिखता है:
public class Foo { public void Test() { var type = typeof(int); dynamic instance = Activator.CreateInstance(type); Boo(instance); } public void Boo(object o) { } }
.Lambda CallSite.Target<System.Action`3[Actionsss.CallSite,ConsoleApp12.Foo,System.Object]>( Actionsss.CallSite $$site, ConsoleApp12.Foo $$arg0, System.Object $$arg1) { .Block() { .If ($$arg0 .TypeEqual ConsoleApp12.Foo && $$arg1 .TypeEqual System.Int32) { .Return #Label1 { .Block() { .Call $$arg0.Boo((System.Object)((System.Int32)$$arg1)); .Default(System.Object) } } } .Else { .Default(System.Void) }; .Block() { .Constant<Actionsss.Ast.Expression>(IIF((($arg0 TypeEqual Foo) AndAlso ($arg1 TypeEqual Int32)), returnUnamedLabel_0 ({ ... }) , default(Void))); .Label .LabelTarget CallSiteBinder.UpdateLabel: }; .Label .If ( .Call Actionsss.CallSiteOps.SetNotMatched($$site) ) { .Default(System.Void) } .Else { .Invoke (((Actionsss.CallSite`1[System.Action`3[Actionsss.CallSite,ConsoleApp12.Foo,System.Object]])$$site).Update)( $$site, $$arg0, $$arg1) } .LabelTarget #Label1: } }
हमारे लिए सबसे महत्वपूर्ण ब्लॉक:
.If ($$arg0 .TypeEqual ConsoleApp12.Foo && $$arg1 .TypeEqual System.Int32)
$ $ arg0 और
$$ arg1 ऐसे पैरामीटर हैं जिनके साथ
लक्ष्य कहा जाता है:
Foo.o__0.p__0.Target((CallSite) Foo.o__0.p__0, <b>this</b>, <b>instance</b>);
मानव में अनुवादित, इसका अर्थ निम्न है:
हमने पहले ही सत्यापित कर लिया है कि यदि पहला पैरामीटर
Foo प्रकार का है और दूसरा
Int32 है , तो आप सुरक्षित रूप से
Boo ((ऑब्जेक्ट) $$ arg1) को कॉल कर सकते हैं।
.Return #Label1 { .Block() { .Call $$arg0.Boo((System.Object)((System.Int32)$$arg1)); .Default(System.Object) }
नोट: बाध्यकारी त्रुटि के मामले में, लेबल 1 ब्लॉक कुछ इस तरह दिखता है: .Return #Label1 { .Throw .New Microsoft.CSharp.RuntimeBinderException("NoSuchMember")
इन जांचों को
अड़चन कहा जाता है। दो प्रकार के
प्रतिबंध हैं : ऑब्जेक्ट के प्रकार और ऑब्जेक्ट के विशिष्ट उदाहरण द्वारा (ऑब्जेक्ट बिल्कुल समान होना चाहिए)। यदि कम से कम एक प्रतिबंध विफल रहता है, तो हमें वैधता के लिए गतिशील अभिव्यक्ति को फिर से जांचना होगा, इसके लिए हम
अपडेट प्रतिनिधि को कॉल करेंगे। हमारे पास पहले से ज्ञात योजना के अनुसार, वह नए प्रकारों के साथ बाध्यकारी प्रदर्शन करेगा और
लक्ष्य में नए
एक्सप्रेशनट्री को बचाएगा।
कैश
हमें पहले ही पता चला है कि
टारगेट एक
L0 कैश है । हर बार
टारगेट को कहा जाता है, पहली चीज जो हम करेंगे वह इसमें पहले से ही संग्रहीत प्रतिबंधों से गुजरती है। यदि प्रतिबंध विफल हो जाते हैं और एक नया बंधन उत्पन्न होता है, तो पुराना नियम
L1 और
L2 पर एक साथ चला जाता है। भविष्य में, जब आप
L0 कैश को याद करते हैं, तो
L1 और
L2 से नियमों को तब तक खोजा जाएगा, जब तक कि उपयुक्त नहीं मिल जाता है।
- L1 : पिछले दस नियम जो L0 को छोड़ चुके हैं (सीधे CallSite में संग्रहीत)
- L2 : एक विशिष्ट बाइंडर उदाहरण (जो CallSiteBinder है , प्रत्येक CallSite के लिए अद्वितीय है) का उपयोग करके बनाए गए पिछले 128 नियम
अब हम अंत में इन विवरणों को एक पूरे में जोड़ सकते हैं और एल्गोरिथ्म के रूप में वर्णन कर सकते हैं कि क्या होता है जब
Foo.Bar (someDynamicObject) कहा जाता है :
1. एक बाइंडर बनाया गया है जो उनके हस्ताक्षरों के स्तर पर संदर्भ और तथाकथित विधि को याद करता है
2. पहली बार ऑपरेशन कहा जाता है,
एक्सप्रेशनट्री बनाई जाती है, जो स्टोर करता है:
२.१
मर्यादा । इस मामले में, ये मौजूदा बाध्यकारी मापदंडों के प्रकार पर दो प्रतिबंध होंगे
2.2
उद्देश्य समारोह : या तो
कुछ अपवाद फेंक दें (इस मामले में यह असंभव है, क्योंकि कोई भी
गतिशील वस्तु को सफलतापूर्वक ले जाएगा) या
बार विधि के लिए एक कॉल
3. परिणामी अभिव्यक्ति को संकलित और निष्पादित करें
4. जब आप ऑपरेशन को याद करते हैं, तो दो विकल्प संभव हैं:
4.1
सीमाएं काम की : बस
बार को कॉल करें
4.2
सीमाएं काम नहीं करती थीं : नए बाध्यकारी मापदंडों के लिए चरण 2 को दोहराएं
इसलिए,
प्रबंधित प्रकार के उदाहरण के साथ, यह लगभग स्पष्ट हो गया कि अंदर से कितना
गतिशील काम करता है। वर्णित मामले में, हम कभी भी कैश को याद नहीं करेंगे, क्योंकि प्रकार हमेशा एक ही * होते हैं, इसलिए
कॉलसइट को इनिशियलाइज़ किए
जाने पर अपडेट बिल्कुल एक बार कॉल
किया जाएगा । फिर, प्रत्येक कॉल के लिए, केवल प्रतिबंधों की जाँच की जाएगी और उद्देश्य फ़ंक्शन को तुरंत बुलाया जाएगा। यह हमारी स्मृति की टिप्पणियों के साथ उत्कृष्ट समझौता है: कोई संगणना नहीं - कोई लीक नहीं।
* इस कारण से, कंपाइलर हर एक के लिए अपना कॉलसाइट बनाता है: L0 कैश गुम होने की संभावना बेहद कम हैयह पता लगाने का समय है कि यह योजना
मूल COM वस्तुओं के मामले में कैसे भिन्न है। चलो
अभिव्यक्ति पर एक नज़र डालें:
.Lambda CallSite.Target<System.Action`3[Actionsss.CallSite,ConsoleApp12.Foo,System.Object]>( Actionsss.CallSite $$site, ConsoleApp12.Foo $$arg0, System.Object $$arg1) { .Block() { .If ($$arg0 .TypeEqual ConsoleApp12.Foo && .Block(System.Object $var1) { $var1 = .Constant<System.WeakReference>(System.WeakReference).Target; $var1 != null && (System.Object)$$arg1 == $var1 }) { .Return #Label1 { .Block() { .Call $$arg0.Boo((System.__ComObject)$$arg1); .Default(System.Object) } } } .Else { .Default(System.Void) }; .Block() { .Constant<Actionsss.Ast.Expression>(IIF((($arg0 TypeEqual Foo) AndAlso {var Param_0; ... }), returnUnamedLabel_1 ({ ... }) , default(Void))); .Label .LabelTarget CallSiteBinder.UpdateLabel: }; .Label .If ( .Call Actionsss.CallSiteOps.SetNotMatched($$site) ) { .Default(System.Void) } .Else { .Invoke (((Actionsss.CallSite`1[System.Action`3[Actionsss.CallSite,ConsoleApp12.Foo,System.Object]])$$site).Update)( $$site, $$arg0, $$arg1) } .LabelTarget #Label1: } }
यह देखा जा सकता है कि अंतर केवल दूसरे प्रतिबंध में है:
.If ($$arg0 .TypeEqual ConsoleApp12.Foo && .Block(System.Object $var1) { $var1 = .Constant<System.WeakReference>(System.WeakReference).Target; $var1 != null && (System.Object)$$arg1 == $var1 })
यदि
प्रबंधित कोड के मामले में हमारे पास वस्तुओं के प्रकार पर दो प्रतिबंध थे, तो यहां हम देखते हैं कि दूसरा प्रतिबंध
WeakReference के माध्यम से उदाहरणों के तुल्यता की जांच करता है।
नोट: COM ऑब्जेक्ट्स के अलावा इंस्टेंस प्रतिबंध का उपयोग ट्रांसपेरेंटप्रॉक्सी के लिए भी किया जाता हैव्यवहार में, कैश के संचालन के हमारे ज्ञान के आधार पर, इसका मतलब है कि हर बार जब हम लूप में एक
COM ऑब्जेक्ट को फिर से बनाते हैं, तो हम
L0 कैश (और
L1 / L2) को भी याद करेंगे, क्योंकि लिंक वाले पुराने नियम वहां संग्रहीत किए जाएंगे पुराने उदाहरणों के लिए)। पहली धारणा जो सिर के लिए पूछती है: नियम कैश बह रहा है। लेकिन वहां का कोड काफी सरल है और वहां सब कुछ ठीक है: पुराने नियम सही तरीके से हटा दिए गए हैं। उसी समय,
एक्सप्रेशनट्री में
वीकेरफेरेंस का उपयोग करने से
जीसी को अनावश्यक वस्तुओं को इकट्ठा करने से ब्लॉक नहीं किया जाता है।
L1 कैश में नियमों को बचाने के लिए तंत्र: const int MaxRules = 10; internal void AddRule(T newRule) { T[] rules = Rules; if (rules == null) { Rules = new[] { newRule }; return; } T[] temp; if (rules.Length < (MaxRules - 1)) { temp = new T[rules.Length + 1]; Array.Copy(rules, 0, temp, 1, rules.Length); } else { temp = new T[MaxRules]; Array.Copy(rules, 0, temp, 1, MaxRules - 1); } temp[0] = newRule; Rules = temp; }
तो सौदा क्या है? आइए एक परिकल्पना को स्पष्ट करने का प्रयास करें: एक
COM ऑब्जेक्ट को
बांधते समय एक मेमोरी रिसाव कहीं होता है।
प्रयोग, भाग २
फिर, आइए सट्टा निष्कर्षों से प्रयोगों तक जाएं। पहले, कंपाइलर हमारे लिए क्या दोहराता है:
हम जाँच करते हैं:

रिसाव को संरक्षित किया गया है। मेला। लेकिन इसका कारण क्या है? बाइंडर्स के कोड का अध्ययन करने के बाद (जिसे हम कोष्ठक के पीछे छोड़ देते हैं), यह स्पष्ट है कि केवल एक चीज जो हमारी वस्तु के प्रकार को प्रभावित करती है वह प्रतिबंध विकल्प है। शायद यह
COM वस्तुओं का मामला नहीं है, लेकिन एक बांधने की मशीन है? बहुत ज्यादा विकल्प नहीं है, चलो
प्रबंधित प्रकार के लिए कई बाध्यकारी
भड़काते हैं :
while (true) { object instance = Activator.CreateInstance(typeof(int)); var autogeneratedBinder = Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Boo", null, typeof(Foo), new CSharpArgumentInfo[2] { CSharpArgumentInfo.Create( CSharpArgumentInfoFlags.UseCompileTimeType, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }); var callSite = CallSite<Action<CallSite, Foo, object>>.Create(autogeneratedBinder); callSite.Target(callSite, this, instance); }

वाह! लगता है हमने उसे पकड़ लिया। समस्या
COM ऑब्जेक्ट के साथ बिल्कुल भी नहीं है, क्योंकि यह हमें शुरू में लगता था, उदाहरण के लिए सीमाओं के कारण, यह एकमात्र ऐसा मामला है जिसमें हमारे लूप के अंदर कई बार बाइंडिंग होती है। अन्य सभी मामलों में, मैं
L0 कैश उठा और एक बार बांध दिया।
निष्कर्ष
स्मृति रिसाव
यदि आप
डायनामिक वैरिएबल के साथ काम करते हैं जिसमें
देशी COM या
ट्रांसपेरेंटप्रॉक्सी होते हैं, तो उन्हें कभी भी विधि पैरामीटर के रूप में पास न करें। यदि आपको अभी भी ऐसा करने की आवश्यकता है, तो स्पष्ट कास्ट टू
ऑब्जेक्ट का उपयोग करें और फिर कंपाइलर आपके पीछे पड़ जाएगा
गलत :
dynamic com = Activator.CreateInstance(comType);
सही ढंग से :
dynamic com = Activator.CreateInstance(comType);
एक अतिरिक्त सावधानी के रूप में, ऐसी वस्तुओं को जितना संभव हो उतना कम करने की कोशिश करें।
.NET फ्रेमवर्क के सभी संस्करणों के लिए वास्तविक। (अभी के लिए) के लिए बहुत प्रासंगिक नहीं है।
NET कोर , चूंकि
गतिशील COM ऑब्जेक्ट्स के
लिए कोई समर्थन नहीं है ।
उत्पादकता
यह आपके हित में है कि कैश की कमी शायद ही कभी होती है, क्योंकि इस मामले में उच्च-स्तरीय कैश में एक उपयुक्त नियम खोजने की आवश्यकता नहीं है।
L0 कैश में
मिसे मुख्य रूप से संरक्षित प्रतिबंधों के साथ
गतिशील वस्तु के प्रकार के बेमेल के मामले में होगा।
dynamic com = GetSomeObject(); public object GetSomeObject() {
हालांकि, व्यवहार में, आप शायद प्रदर्शन में अंतर को नोटिस नहीं करेंगे जब तक कि इस फ़ंक्शन को कॉल की संख्या लाखों में मापी जाती है या यदि प्रकार की परिवर्तनशीलता असामान्य रूप से बड़ी नहीं है।
L0 कैश पर मिस के मामले में लागत इस प्रकार हैं,
एन प्रकार की संख्या है:
- एन <10। यदि आप याद करते हैं, तो मौजूदा L1 कैश नियमों पर केवल पुनरावृति
- 10 < एन <128 । एल 1 और एल 2 कैश की गणना (अधिकतम 10 और एन पुनरावृत्तियों)। 10 तत्वों की एक सरणी बनाना और आबाद करना
- एन > 128। L1 और L2 कैश पर Iterate करें। 10 और 128 तत्वों के सरणियों को बनाएं और आबाद करें। यदि आप L2 कैश को याद करते हैं, तो पुन: बाध्यकारी
दूसरे और तीसरे मामलों में, जीसी पर भार बढ़ेगा।
निष्कर्ष
दुर्भाग्य से, हमें मेमोरी लीक का वास्तविक कारण नहीं मिला, इसके लिए बाइंडर के एक अलग अध्ययन की आवश्यकता होगी। सौभाग्य से,
WinDbg आगे की जांच के लिए एक संकेत प्रदान करता है:
DLR में कुछ बुरा होता है। पहला कॉलम ऑब्जेक्ट की संख्या है

बोनस
स्पष्ट रूप से रिसाव को रोकने के लिए ऑब्जेक्ट को कास्टिंग क्यों करता है?किसी भी प्रकार को
ऑब्जेक्ट के लिए डाला जा सकता है, इसलिए ऑपरेशन गतिशील होना बंद हो जाता है।
COM ऑब्जेक्ट के फ़ील्ड्स और विधियों के साथ काम करते समय कोई लीक क्यों नहीं है?यह वही है जो
एक्सप्रेशनट्री फील्ड एक्सेस के लिए दिखता है:
.If ( .Call System.Dynamic.ComObject.IsComObject($$arg0) ) { .Return #Label1 { .Dynamic GetMember ComMarks(.Call System.Dynamic2.ComObject.ObjectToComObject($$arg0)) } }