2013-04-21 8 views
7

मैं एक स्थिति है जहाँ कचरा संग्रहण एक यूनिट टेस्ट बनाम एक कंसोल आवेदन की Main विधि में लिखा के रूप में लिखा चल रहा एक ही कोड के बीच अलग-अलग ढंग से व्यवहार किया जा रहा है भर में ठोकर खाई है । मैं इस अंतर के पीछे कारण सोच रहा हूँ।विभिन्न कूड़ा संग्रह व्यवहार

इस स्थिति में, एक सह कार्यकर्ता और मैं कचरा संग्रहण पर एक घटना हैंडलर पंजीकृत करने के प्रभाव पर असहमति में थे। मैंने सोचा कि एक प्रदर्शन को highly rated SO answer पर एक लिंक भेजने से बेहतर प्रदर्शन किया जाएगा। जैसे मैंने यूनिट टेस्ट के रूप में एक सरल प्रदर्शन लिखा था।

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

मैं क्या जानना चाहता हूं कि जीसी कंसोल एप्लिकेशन की Main विधि में चलते समय वस्तुओं को इकट्ठा करने की प्रतीत क्यों नहीं कर रहा है। विधियों को निकालने से GC.Collect पर कॉल और दायरे से बाहर निकलने वाली वस्तु विभिन्न तरीकों से हुई, अपेक्षित व्यवहार बहाल किया गया था।

ये वे वस्तुएं हैं जिनका उपयोग मैंने अपने परीक्षण को परिभाषित करने के लिए किया था। एक घटना के साथ बस एक वस्तु है और एक वस्तु एक घटना हैंडलर के लिए उपयुक्त विधि प्रदान करने वाला एक वस्तु है। दोनों में ग्लोबल वैरिएबल सेट करने वाले फाइनलर्स हैं ताकि आप बता सकें कि उन्हें कब एकत्र किया गया है।

[Test] 
public void TestReference() 
{ 
    { 
     HandlingObject subscriber = new HandlingObject(); 

     { 
      { 
       EventedObject publisher = new EventedObject(); 
       publisher.DoIt += subscriber.Yeah; 
      } 

      GC.Collect(GC.MaxGeneration); 
      GC.WaitForPendingFinalizers(); 
      Thread.MemoryBarrier(); 

      Assert.That(Log, Is.EqualTo(EventedObjectDisposed)); 
     } 

     //Assertion needed for foo reference, else optimization causes it to already be collected. 
     Assert.IsNotNull(subscriber); 
    } 

    GC.Collect(GC.MaxGeneration); 
    GC.WaitForPendingFinalizers(); 
    Thread.MemoryBarrier(); 

    Assert.That(Log, Is.EqualTo(HandlingObjectDisposed)); 
} 

मैं एक नया कंसोल आवेदन की Main विधि करने के लिए ऊपर शरीर चिपकाया, और Trace.Assert आमंत्रण को Assert कॉल परिवर्तित:

private static string Log; 
public const string EventedObjectDisposed = "EventedObject disposed"; 
public const string HandlingObjectDisposed = "HandlingObject disposed"; 

private class EventedObject 
{ 
    public event Action DoIt; 

    ~EventedObject() 
    { 
     Log = EventedObjectDisposed; 
    } 

    protected virtual void OnDoIt() 
    { 
     Action handler = DoIt; 
     if (handler != null) handler(); 
    } 
} 

private class HandlingObject 
{ 

    ~HandlingObject() 
    { 
     Log = HandlingObjectDisposed; 
    } 

    public void Yeah() 
    { 
    } 
} 

यह मेरा परीक्षण (NUnit) है, जो गुजरता है। दोनों समानता दावा विफल हो जाते हैं तो विफल हो जाते हैं। परिणामी मुख्य विधि का कोड here है यदि आप इसे चाहते हैं।

मुझे पता है कि जब जीसी होता है तो उसे गैर-निर्धारिती के रूप में माना जाना चाहिए और आमतौर पर जब कोई ऐसा होता है तो एक आवेदन स्वयं से संबंधित नहीं होना चाहिए। सभी मामलों में कोड रिलीज़ मोड में संकलित किया गया था और .NET 4.5 को लक्षित किया गया था।

संपादित करें: अन्य बातें मैंने कोशिश की

  • परीक्षा पद्धति static बनाने के बाद से NUnit कि समर्थन करता है; परीक्षण अभी भी काम किया।
  • मैं भी कार्यक्रम पर एक उदाहरण विधि में निकालने पूरे मुख्य विधि और कहा कि फोन करने की कोशिश की। दोनों आवेदक अभी भी असफल रहे।
  • [STAThread] या [MTAThread] के साथ this के मामले में एक फर्क पड़ता है। दोनों अभी भी विफल रहा है पाता है।
  • @ मू-रस के सुझावों के आधार पर:
    • मैं कंसोल अनुप्रयोग के लिए NUnit संदर्भित ताकि मैं इस्तेमाल कर सकते हैं NUnit का दावा है, वे विफल रहे।
    • मैंने टेस्ट, टेस्ट क्लास, Main विधि, और Main विधि स्थिर दोनों कक्षाओं की दृश्यता में विभिन्न परिवर्तनों की कोशिश की। कोई परिवर्तन नहीं होता है।
    • मैंने टेस्ट क्लास स्थिर और Main विधि स्थिर युक्त कक्षा बनाने की कोशिश की। कोई परिवर्तन नहीं होता है।
+0

जब आप 'जीसी.कोलेक्ट()' का उपयोग अपने डिफ़ॉल्ट पैरामीटर के साथ करते हैं और पीढ़ी निर्दिष्ट नहीं करते हैं तो इसका क्या परिणाम होता है? –

+0

@ म्यू-रस परीक्षण अभी भी गुजरता है, कंसोल एप्लिकेशन का दावा अभी भी असफल हो जाता है, कंसोल से विधियों को निकालने के लिए आवेदन अभी भी सफल होने के लिए आवेषण का कारण बनता है। – vossad01

+0

मुझे आश्चर्य है कि अंतर इस तथ्य के कारण है कि 'मुख्य' एक स्थिर वर्ग के भीतर है ... मुझे यकीन नहीं है, लेकिन इसके अलावा और 'ट्रेस' बनाम 'एसेर्ट' के व्यवहार के अलावा मैं विचारों से बाहर हूं। क्या आपने परीक्षण को स्थिर आंकड़ों के भीतर से चलाने का प्रयास किया है? –

उत्तर

6

निम्नलिखित कोड एक अलग विधि से निकाला गया था, तो परीक्षण अधिक के रूप में आप की उम्मीद व्यवहार करने की संभावना होगी। संपादित करें: ध्यान दें कि सी # भाषा विनिर्देश के शब्द को इस परीक्षा को पास करने की आवश्यकता नहीं है, भले ही आप कोड को एक अलग विधि में निकाल दें।

 { 
      EventedObject publisher = new EventedObject(); 
      publisher.DoIt += subscriber.Yeah; 
     } 

विनिर्देश की अनुमति देता है, लेकिन आवश्यकता नहीं है कि publisher तुरंत इस ब्लॉक के अंत में जी सी के लिए पात्र हो, तो आप इस तरह से कि आप इसे यहां एकत्र किया जा सकता मानते हैं में कोड नहीं लिखना चाहिए।

संपादित करें: ECMA-334 (सी # भाषा विनिर्देश) §10.9 स्वत: स्मृति प्रबंधन (जोर मेरा)

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

+0

बस यह सुनिश्चित करने के लिए कि मैं समझता हूं, spec _require_ 'प्रकाशक जीसी के लिए उपलब्ध होगा यदि वह कोड बिट अलग तरीके से है, लेकिन यह नहीं है कि यह केवल' {''} 'से घिरे कोड कोड में है? – vossad01

+0

@ vossad01 हाँ, आप सही हैं। हालांकि, जब अंतिमकर्ता चलाया जाता है, तब भी यह कोई आवश्यकता नहीं करता है, भले ही 'जीसी। कोलेक्ट' कहा जाता है। इसलिए, कोड को स्थानांतरित करने के बावजूद परीक्षण विफल हो सकता है और यह सी # कंपाइलर या .NET कार्यान्वयन में एक बग इंगित नहीं करेगा। –

+0

'GC.WaitForPendingFinalizers 'यह सुनिश्चित नहीं करता है कि अंतिमकर्ता चलाया जाए क्योंकि' जीसी.कोलेक्ट' नहीं है? – vossad01

1

समस्या यह है कि यह एक सांत्वना अनुप्रयोग है नहीं है - एक डिबगर संलग्न साथ - समस्या यह है कि आप की संभावना दृश्य स्टूडियो के माध्यम से चला रहे हैं है! और/या आप कंसोल ऐप को डीबग बिल्ड के रूप में संकलित कर रहे हैं।

सुनिश्चित करें कि आप रिलीज बिल्ड को संकलित कर रहे हैं। फिर Debug -> Start Without Debugging पर जाएं, या Ctrl + F5 दबाएं, या कमांड लाइन से अपना कंसोल एप्लिकेशन चलाएं। कचरा कलेक्टर अब उम्मीद के रूप में व्यवहार करना चाहिए।

यही कारण है कि एरिक लिपर्ट आपको C# Performance Benchmark Mistakes, Part One में डीबगर में किसी भी पर्फ बेंचमार्क चलाने की याद दिलाता है।

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

एरिक की लेख श्रृंखला में अनुस्मारक के बहुत सारे आपके परिदृश्य पर लागू होते हैं। यदि आप और पढ़ने में रुचि रखते हैं, तो यहां two, three और four भागों के लिए लिंक दिए गए हैं।

संबंधित मुद्दे