2012-11-03 25 views
5

कभी-कभी उपयोगकर्ता टाइमर का एक बड़ा सेट शेड्यूल करना चाहते हैं और उन टाइमर के संदर्भों को प्रबंधित नहीं करना चाहते हैं।
यदि उपयोगकर्ता टाइमर का संदर्भ नहीं देता है, तो टाइमर को निष्पादित करने से पहले जीसी द्वारा एकत्र किया जा सकता है।
मैं कक्षा टाइमर बनाया नव निर्मित टाइमर के लिए एक जगह धारक के रूप में सेवा करने के लिए:टाइमर प्रतिनिधि के आसपास मेमोरी लीक

static class Timers 
{ 
    private static readonly ILog _logger = LogManager.GetLogger(typeof(Timers)); 

    private static readonly ConcurrentDictionary<Object, Timer> _timers = new ConcurrentDictionary<Object, Timer>(); 

    /// <summary> 
    /// Use this class in case you want someone to hold a reference to the timer. 
    /// Timer without someone referencing it will be collected by the GC even before execution. 
    /// </summary> 
    /// <param name="dueTime"></param> 
    /// <param name="action"></param> 
    internal static void ScheduleOnce(TimeSpan dueTime, Action action) 
    { 
     if (dueTime <= TimeSpan.Zero) 
     { 
      throw new ArgumentOutOfRangeException("dueTime", dueTime, "DueTime can only be greater than zero."); 
     } 
     Object obj = new Object(); 

     Timer timer = new Timer(state => 
     { 
      try 
      { 
       action(); 
      } 
      catch (Exception ex) 
      { 
       _logger.ErrorFormat("Exception while executing timer. ex: {0}", ex); 
      } 
      finally 
      { 
       Timer removedTimer; 
       if (!_timers.TryRemove(obj, out removedTimer)) 
       { 
        _logger.Error("Failed to remove timer from timers"); 
       } 
       else 
       { 
        removedTimer.Dispose(); 
       } 
      } 
     }); 
     if (!_timers.TryAdd(obj, timer)) 
     { 
      _logger.Error("Failed to add timer to timers"); 
     } 
     timer.Change(dueTime, TimeSpan.FromMilliseconds(-1)); 
    } 
} 

मैं हटा दिया टाइमर निपटान नहीं है, तो यह एक स्मृति रिसाव के साथ परिणाम है।
ऐसा लगता है कि टाइमर को _timers संग्रह से हटा दिए जाने के बाद टाइमर के प्रतिनिधि को संदर्भित किया जा रहा है।

सवाल यह है कि, अगर मैं टाइमर का निपटान नहीं करता हूं तो मुझे मेमोरी रिसाव क्यों मिलती है?

+0

शायद मैं, क्योंकि यह लग रहा है आप सोच रहे हैं की तरह क्यों कुछ असामान्य व्यवहार करता है जब आप इसे गाली समझ में नहीं आता कि तुम क्या कह रहे हैं ... –

+0

मैं समझता हूं कि दस्तावेज कहता है कि घटकों का निपटान किया जाना चाहिए। मैं अभी भी उत्सुक हूं कि जीसी से निपटान विधि को बुलाए बिना टाइमर और दिए गए प्रतिनिधि को इकट्ठा करने से रोकता है। –

उत्तर

9

Timer को GCHandle द्वारा जीवित रखा गया है जो टाइमर द्वारा स्वयं बनाया गया है। यह एक .NET मेमोरी प्रोफाइलर का उपयोग करके परीक्षण किया जा सकता है। बदले में Timer प्रतिनिधि को जिंदा रखेगा, जो बाकी को जीवित रखेगा।

एक GCHandle उद्देश्य यह है कि इस्तेमाल किया जा सकता का एक विशेष प्रकार का है "चाल" कचरा कलेक्टर नहीं पहुंचा जा सकता वस्तुओं को जीवित रखने के लिए।

आप कर सकते थे वास्तव में तरह-के प्रोफाइलर का उपयोग किए बिना परीक्षण इस:

var a = new ClassA(); 
var timer = new Timer(a.Exec); 

var refA = new WeakReference(a); 
var refTimer = new WeakReference(timer); 

a = null; 
timer = null; 

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

Console.WriteLine(refA.IsAlive); 
Console.WriteLine(refTimer.IsAlive); 
+0

यह दिलचस्प है। मुझे आश्चर्य है कि क्यों System.Threading.Timer प्रलेखन कहते हैं:। "जब तक आप एक टाइमर का उपयोग कर रहे हैं, आप इसे करने के लिए एक संदर्भ रखना चाहिए किसी भी प्रबंधित वस्तु के साथ के रूप में, एक टाइमर कचरा संग्रहण के अधीन है जब वहाँ यह करने के लिए कोई संदर्भ हैं तथ्य यह है कि एक टाइमर अभी भी सक्रिय है इसे एकत्रित होने से नहीं रोकता है। " मैंने यह भी देखा कि टाइमर को कोई संदर्भ नहीं रखा गया है, तो टाइमर निष्पादित नहीं किया जा सकता है। –

+0

ऐसा लगता है कि डेवलपर्स ने इसे काम करने का इरादा किया था। यदि मैं अपने परीक्षण प्रोग्राम को .NET 4.0 या उच्चतर में चलाता हूं तो ऐसा लगता है कि ऑब्जेक्ट्स एकत्र किए जाते हैं। –

4

TimersComponents हैं। जैसे कि आप उनके साथ किए जाने पर Dispose पर कॉल करना होगा।

the documentation से

:

एक घटक, अपने Dispose विधि के लिए कॉल द्वारा स्पष्ट रूप से संसाधनों को रिहा करना चाहिए Finalize विधि के लिए एक अंतर्निहित कॉल के माध्यम से स्वचालित स्मृति प्रबंधन का इंतजार किए बिना। जब Container का निपटारा किया जाता है, तो Container के सभी घटकों का भी निपटान किया जाता है।

हिस्सा "जब एक Container निपटान किया जाता है, Container भीतर सभी घटकों को भी निपटाया जाता है।" एक प्रपत्र के निपटान विधि में देखा जा सकता है जब यह कहता है:

if (disposing && (components != null)) 
{ 
    components.Dispose(); 
} 

तो उम्मीद नहीं है टाइमर फार्म के साथ निपटारा करने के लिए जब तक वे घटकों के लिए जोड़ा गया था।

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

जो मैं समझता हूं कि निपटान मॉडल को लगता है कि प्रोग्राम बंद होने की गति तेज है (क्योंकि रन टाइम डाउन टाइम के दौरान कचरा इकट्ठा कर सकता है) जबकि अभी भी अप्रबंधित कोड को निष्पादित करने की इजाजत है। यदि आप बड़ी संख्या में डीडीएल आयात करते हैं तो आप यह देखना शुरू कर देंगे कि सिस्टम किस तरह से काम करता है।

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

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

+0

सही। लेकिन टाइमर जीसी द्वारा एकत्र किया जाएगा और फिर अंतिमकर्ता को कॉल करेगा जो प्रतिनिधि और बाकी संसाधनों को जारी करेगा। मेरा सवाल यह है कि उस मामले में निपटान विधि की आवश्यकता क्यों है। –

+0

@OronNadiv एक टाइमर अप्रबंधित कोड (ओएस के टाइमर एपीआई) की ओर इशारा किया है जब तक कि उन की अब जरूरत नहीं है तो यह निपटान नहीं कर सकते हैं। अंतिमकर्ता ऑब्जेक्ट पर तब तक नहीं चलेगा जब तक कि निपटान को पहले नहीं कहा जाता है या प्रोग्राम अप्रबंधित कोड के इन मौजूदा संदर्भों के कारण बाहर निकल रहा है। – Trisped

+1

ऐसा लगता है कि वह उपयोग कर रहा है 'System.Threading.Timer' है, जो एक' Component' नहीं है। –

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