2010-08-23 9 views
8

मान लीजिए कि निम्नलिखित कोड है:क्या "प्रोत्साहित" .NET कचरा संग्रह के संदर्भों को रद्द करने का कोई लाभ है?

foreach (...) 
{ 
    List<int> localList = new List<int>(100); 

    // do stuff 

    localList = null; 
} 

समय-समय पर, मैं संदर्भ शून्य पर बस के रूप में प्रक्रिया के बारे में बाहर निकलने के लिए (अर्थात, वापसी) या इस मामले में है परीक्षा रहा हूँ, यह है बस के रूप में लूप के बारे में। ऐसा करने के लिए कोई फायदा है, भले ही एक छोटा सा?

+5

जेआईटी अनुकूलक बस कथन को हटा देता है। –

+1

@ हंस, नाइटपिक: यह "सामानों" में कोड पर निर्भर करता है। यदि स्थानीय सूची को बंद या पुनरावर्तक में उठाया जाता है तो रेखा बनी रहती है। – JaredPar

+1

@ जेरेड: मुझे ऐसा नहीं लगता है, मूल्य ऑटो-जेनरेटेड क्लास के एक क्षेत्र में कॉपी किया जाएगा। –

उत्तर

12

स्थानीय चर के लिए ऐसा करने का कोई फायदा नहीं है। सीएलआर, जेआईटी के माध्यम से जानता है, ठीक उसी समय जब एक स्थानीय चर का उपयोग किसी विधि के भीतर नहीं किया जाता है और इसलिए एकत्रित किया जाता है।

रेमंड चेन ने हाल ही में ऑब्जेक्ट्स एकत्रित होने पर गहराई से ब्लॉग आलेख किया था। यह विस्तार से इस परिदृश्य को शामिल किया गया और लायक है

लेकिन वहाँ इस नियम के अपवाद के एक जोड़े है पढ़ें। यदि प्रश्न में स्थानीय चर को बंद या एक पुनरावर्तक में पकड़ा जाता है तो वैरिएबल को हां बाहर करने का प्रभाव पड़ता है। अर्थात् क्योंकि स्थानीय अब स्थानीय नहीं है बल्कि इसके बजाय एक क्षेत्र है और इसमें विभिन्न जीसी अर्थशास्त्र हैं।

+0

+1: यह एक महान ब्लॉग आलेख था। –

+0

मुझे लगता है कि स्थानीय चर के शून्य (आईएनजी) के मामले में एक मामला है: 'var bo = new BigObject(); के लिए (int i = 0; i <100000; i ++) {if (i <10) {bo.DoSomething()}} 'यहां' x' 'null'ed हो सकता है जब bo == 10. यह काफी असंभव है कि जीसी वास्तव में क्या हो रहा है "इसे देखने" और हमारे लिए इसे मुक्त करने में सक्षम हो जाएगा। – xanatos

4

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

+0

क्या आप इसके बारे में निश्चित हैं, या यह केवल इसलिए है क्योंकि ऑप्टिमाइज़र "x = null" जैसे बयान छोड़ देता है जो एक चर के लिए दिए गए दायरे में अंतिम उपयोग है? – Mark

+0

@ डीन जे, जीसी के उद्देश्य के लिए परिवर्तनीय जीवनकाल अक्सर इसके दायरे के दायरे से बहुत छोटा होता है। विधि के भीतर अब इसका उपयोग नहीं होने के तुरंत बाद यह आजीवन समाप्त होता है। स्कोप सी # निर्माण है जिसका जीसी ऑपरेशन के लिए कोई वास्तविक अर्थ नहीं है। – JaredPar

+0

@ एडम: मैं इस जवाब में जॉन स्कीट को परेशान कर रहा हूं, जो कुछ मैंने बहुत पहले पढ़ा था। कुछ और दिखने के बाद, उन्होंने 1.1 के लिए अपने बयान संशोधित किए, उन्होंने कहा कि वह सोचते हैं कि 1.1 रनटाइम इस संबंध में 1.0 रनटाइम से अधिक स्मार्ट है। निश्चित रूप से निहितार्थ है, क्योंकि जेरेडपार कहता है कि जीवनकाल संलग्न क्षेत्र से छोटा हो सकता है। – Mark

2

यदि घोषणा आपके जैसे ब्लॉक के भीतर नहीं है। एक बार यह दायरे से बाहर हो जाने पर संदर्भ स्वचालित रूप से निरस्त हो जाएगा और जीसी'd होगा।

+0

ठीक है, इसलिए यदि मैं बाहर निकलता हूं, तो आप कह रहे हैं कि कोई लाभ नहीं है क्योंकि यह दायरे से बाहर हो जाएगा। हालांकि, अगर मैं लूप करता हूं, तो यह गुंजाइश में रहता है, भले ही मुझे इसकी आवश्यकता नहीं है। यह मेरे लिए कचरा है और मुझे एक नया उदाहरण चाहिए। शायद अगर मैं इसे खो देता हूं तो स्मृति जल्द ही मुक्त हो जाएगी? – H2ONaCl

+0

यह हो सकता है, लेकिन कोई गारंटी नहीं है, क्योंकि यह जीसी तक छोड़ दिया गया है। यदि आप स्मृति समस्याओं में चल रहे हैं, तो निश्चित रूप से एक मेमोरी प्रोफाइलर (उदाहरण के लिए एएनटीएस प्रोफाइलर) को आजमाएं क्योंकि एक विधि में निरर्थकता शायद आपकी रजत बुलेट नहीं होगी। – Pat

1

नहीं। जीसी को अपना काम करने दें और यदि आपको आवश्यकता हो तो केवल इसकी सहायता करें।

2

मैंने a long and exhaustive blog article answering this question लिखा था।

संक्षेप में, सामान्य उत्तर "नहीं; कोई लाभ नहीं है"। जब वे अब जरूरत हैं

  • स्टेटिक क्षेत्रों अशक्त करने के लिए सेट किया जाना चाहिए - जब तक प्रक्रिया बंद हो रहा है, ऐसी स्थिति में अशक्त करने के लिए स्थिर क्षेत्रों की स्थापना अनावश्यक है: हालांकि, वहाँ कुछ विशेष मामले हैं।
  • स्थानीय चरों को शायद ही कभी शून्य पर सेट करने की आवश्यकता है। केवल एक अपवाद है: गैर-माइक्रोसॉफ्ट सीएलआर पर चलने पर स्थानीय चर सेट करने के लिए फायदेमंद हो सकता है।
  • इंस्टेंस फ़ील्ड को शायद ही कभी शून्य पर सेट करने की आवश्यकता है। केवल एक अपवाद है: रेफरेंसिंग ऑब्जेक्ट को संदर्भित ऑब्जेक्ट से बाहर निकलने की अपेक्षा होने पर एक इंस्टेंस फ़ील्ड शून्य पर सेट किया जा सकता है। [ध्यान दें कि IDisposable.Dispose में उदाहरण फ़ील्ड को शून्य करने के अर्ध-सामान्य अभ्यास इस परीक्षण को पूरा नहीं करते हैं, और उन्हें प्रोत्साहित नहीं किया जाना चाहिए]।

निष्कर्ष में: आम तौर पर, कचरा कलेक्टर की मदद करने के लिए शून्य को चर सेट करने की अनुशंसा नहीं की जाती है। यदि इसे आवश्यक समझा जाता है, तो एक असामान्य स्थिति मौजूद है और इसे कोड में सावधानी से दस्तावेज किया जाना चाहिए।

+0

मैं हाल ही में डिस्पोजेबल में नलिका नहीं करने के अपवाद में आया था। एक ऐसी वेब जो एक वेबस्पॉन्स पर आधारित "लोड" होती है, लेकिन जो भी कैशबल थी। निपटान() ने सुनिश्चित किया कि यह वेबस्पॉन्स और स्ट्रीम को साफ़ कर दिया गया है, लेकिन बाद में इसका उपयोग फिर से किया जा सकता है (अर्थात् अपरिवर्तनीय) लेकिन कुछ समय के लिए नहीं हो सकता है (इसलिए निपटान() अभी भी आवश्यक था)। इसलिए इसे कैशिंग के कारण, अपने असली जीवनकाल में जल्दी ही निपटाया जा सकता है। –

+0

@ जोन: हाँ, मैं देख सकता हूं कि यह एक अपवाद कहाँ होगा। यह एक बहुत ही असामान्य वर्ग है, हालांकि ('निपटान' के बाद नियमित उपयोग की इजाजत देता है)। –

+0

हां, यह पूरी तरह से बाहरी दृश्य के कारण था क्योंकि यह "अल्पकालिक" था लेकिन वास्तव में यह लंबे समय तक रहता था। वास्तव में जो कुछ भी है, उसके अलावा किसी अन्य चीज के रूप में नकल करने में, यह दोनों की कुछ विशेषताओं को प्राप्त करता है। उस ने कहा, मैंने अभी भी उपरोक्त लिखने के बाद से नलिंग (और कुछ अन्य क्रुफ्ट) को दोबारा करने का प्रबंधन किया है :) –

2

यदि किसी वर्ग में फाइनलाइज़र होता है, तो किसी भी गैर-शून्य ऑब्जेक्ट-रेफरेंस फ़ील्ड संदर्भित वस्तुओं को अन्यथा की तुलना में लंबे समय तक आयोजित करने का कारण बनेंगे।यदि ऑब्जेक्ट्स को अंतिम रूप से चलाने से पहले अनावश्यक माना जाता है, तो ऑब्जेक्ट-रेफरेंस फ़ील्ड्स को साफ़ करने से ऑब्जेक्ट्स को "पीढ़ी" जितनी जल्दी हो सके उतनी जल्दी इकट्ठा करने की अनुमति मिल जाएगी। यह एक बड़ी जीत हो सकती है।

यदि किसी ऑब्जेक्ट का जीवनकाल (उपयोगी या नहीं) किसी ऑब्जेक्ट के उपयोगी जीवनकाल को पार करने की अपेक्षा करता है जिसके संदर्भ में इसका संदर्भ होता है, तो अनिवार्य रूप से एक संदर्भ धारण करने से बाद की वस्तु को पूर्व तक एकत्रित होने से रोक दिया जाएगा (यानी संदर्भ बाद के ऑब्जेक्ट को बेकार बनने के बाद भी रखा जाएगा)। संदर्भ साफ़ करना उस समस्या से बच जाएगा।

यदि vb.net में लिखी गई कक्षा में "एवेन्ट्स वेरिएबल्स" है, तो उन्हें किसी भी समय ऑब्जेक्ट रखने वाले ऑब्जेक्ट को बेकार हो जाना चाहिए (उन्हें कुछ भी सेट नहीं किया जाना चाहिए)। एक वर्ग को कचरा-संग्रह नहीं किया जा सकता है, जबकि इसमें "एवेवेंट्स वेरिएबल" में लाइव ऑब्जेक्ट का संदर्भ होता है। यदि उदा। एक गणक के पास एक अंतर्निहित संग्रह के लिए "WithEvents" संदर्भ होता है (उदाहरण के लिए यदि संग्रह बदल जाता है तो यह घटनाएं प्राप्त कर सकता है) और इसका निपटान हैंडलर अंतर्निहित संग्रह के संदर्भ को स्पष्ट नहीं करता है, तो गणनाकर्ता को तब तक जीवित रखा जाएगा अंतर्निहित संग्रह है। यदि संग्रह कई बार गिनती है, तो यह एक विशाल स्मृति रिसाव हो सकता है।

0

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

यह महत्वपूर्ण है, क्योंकि इससे रिलीज निर्माण में क्या हो रहा है के बारे में झूठा विचार हो सकता है।

0

और प्रतिक्रिया है: हाँ यदि आप जानते हैं कि आप क्या कर रहे हैं! लेकिन यह समयपूर्व अनुकूलन (कई लोगों के लिए सभी बुराइयों की जड़) है, इसलिए आपको इसे तब तक नहीं करना चाहिए जब तक आपको वास्तव में स्मृति की आवश्यकता न हो।

अब, मैं केवल दो मामलों का विश्लेषण करूंगा जहां यह उपयोगी हो सकता है। एक वस्तु का

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

  • जटिल तरीकों में फ़ील्ड। यह दूसरों के कहने के विपरीत है, लेकिन मेरे पास एक उदाहरण है, इसलिए मुझे पता है कि मैं सही हूं :-) अब, आमतौर पर, क्रमिक विधि में, जीसी देख सकता है कि अब स्थानीय संदर्भ का उपयोग नहीं किया जाता है (तकनीकी रूप से महत्वपूर्ण हिस्सा तब होता है जब इसे और नहीं पढ़ा जाता है। और चर के कॉलिंग विधियों को "पढ़ना" होता है। एक चर को लिखना गिनती नहीं है, क्योंकि यदि कोई नया मान नहीं पढ़ता है, तो इसे लिखना बेकार है)

लेकिन मैंने अनुक्रमिक विधि कहा है। और अनुक्रमिक तरीकों? कोशिश करते हैं! (स्पष्ट रूप से मैंने इसे पहले ही कर लिया है! मैं ideone उदाहरण नहीं बनाऊंगा क्योंकि मोनो का जीसी .NET के जीसी से अलग है)।

रिलीज मोड में कोड के इस टुकड़े संकलित करें और डीबगर (ताकि Ctrl-F5) के बिना lauch और निरीक्षण परिणाम:

using System; 

namespace ConsoleApplication17 
{ 
    class Program 
    { 
     static void Main() 
     { 
      const int cycles = 1000000; 

      { 
       string str = new string(' ', 10000000); 

       Console.WriteLine("Let's see when the GC will free the memory"); 
       Console.WriteLine("Start: {0}", GC.GetTotalMemory(true)); 

       for (int i = 0; i < 1000000; i++) 
       { 
        if (i == cycles - 1) 
        { 
         Console.WriteLine("Near end: {0}", GC.GetTotalMemory(true)); 
        } 

        //Here we reference the string, 
        //but only in the first 100 iterations 
        if (i < 100 && str[str.Length - 1] == 'c') 
        { 
         throw new Exception(); 
        } 
       } 

       Console.WriteLine("End: {0}", GC.GetTotalMemory(true)); 
      } 

      Console.WriteLine(); 

      { 
       string str = new string(' ', 10000000); 

       Console.WriteLine("Let's do the work for him"); 
       Console.WriteLine("Start: {0}", GC.GetTotalMemory(true)); 

       for (int i = 0; i < 1000000; i++) 
       { 
        if (i == cycles - 1) 
        { 
         Console.WriteLine("Near end: {0}", GC.GetTotalMemory(true)); 
        } 

        //Here we reference the string, 
        //but only in the first 100 iterations 
        if (i < 100 && str[str.Length - 1] == 'c') 
        { 
         throw new Exception(); 
        } 
        else if (i == 100) 
        { 
         str = null; 
         Console.WriteLine("Just nullified the string: {0}", GC.GetTotalMemory(true)); 
        } 
       } 

       Console.WriteLine("End: {0}", GC.GetTotalMemory(true)); 
      } 

      Console.ReadKey(); 
     } 
    } 
} 

परिणाम:

Let's see when the GC will free the memory 
Start: 20042264 
Near end: 20042888 
End: 42872 

Let's do the work for him 
Start: 20042888 
Just nullified the string: 42872 
Near end: 42872 
End: 42872 

एक स्पष्टीकरण: यह कार्यक्रम चक्रीय कार्यक्रम का एक उदाहरण है जहां चक्र के बाहर एक बड़ी वस्तु शुरू की जाती है (for चक्र) और बड़ी वस्तु केवल चक्र के हिस्से में उपयोग की जाती है ले (उदाहरण के लिए पहले 100 पुनरावृत्तियों में)। स्पष्ट रूप से जीसी आसानी से नहीं देख सकता है कि पुनरावृत्ति 99 (100 वें पुनरावृत्ति, 0-99) के अंत से ऑब्जेक्ट का संदर्भ नहीं दिया जाएगा।

लेकिन शब्दों को याद समय से पहले अनुकूलन और सभी बुराइयों की जड़! :-)

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