2011-02-10 29 views
60

ऐसा लगता है कि System.Timers.Timer उदाहरणों कुछ तंत्र द्वारा जीवित रखा जाता है, लेकिन System.Threading.Timer उदाहरणों नहीं हैं।सिस्टम क्यों है। टिमर्स। टिमर जीसी से बचें लेकिन सिस्टम नहीं। थ्रेडिंग। टिमर?

नमूना कार्यक्रम, एक आवधिक System.Threading.Timer और साथ स्वतः रीसेट System.Timers.Timer:

class Program 
{ 
    static void Main(string[] args) 
    { 
    var timer1 = new System.Threading.Timer(
     _ => Console.WriteLine("Stayin alive (1)..."), 
     null, 
     0, 
     400); 

    var timer2 = new System.Timers.Timer 
    { 
     Interval = 400, 
     AutoReset = true 
    }; 
    timer2.Elapsed += (_, __) => Console.WriteLine("Stayin alive (2)..."); 
    timer2.Enabled = true; 

    System.Threading.Thread.Sleep(2000); 

    Console.WriteLine("Invoking GC.Collect..."); 
    GC.Collect(); 

    Console.ReadKey(); 
    } 
} 

जब मैं इस कार्यक्रम (.NET 4.0 क्लाइंट, रिलीज, डीबगर के बाहर), केवल System.Threading.Timer GC'ed है चलाएँ:

Stayin alive (1)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Invoking GC.Collect... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 
Stayin alive (2)... 

संपादित: मैं जॉन के जवाब नीचे स्वीकार कर लिया है, लेकिन मैं इसे पर थोड़ा व्याख्या करने के लिए करना चाहता था।

जब (Sleep पर एक ब्रेकपाइंट साथ) के ऊपर नमूना कार्यक्रम चल रहा है, यहाँ सवाल में वस्तुओं के राज्य और GCHandle तालिका है:

!dso 
OS Thread Id: 0x838 (2104) 
ESP/REG Object Name 
0012F03C 00c2bee4 System.Object[] (System.String[]) 
0012F040 00c2bfb0 System.Timers.Timer 
0012F17C 00c2bee4 System.Object[] (System.String[]) 
0012F184 00c2c034 System.Threading.Timer 
0012F3A8 00c2bf30 System.Threading.TimerCallback 
0012F3AC 00c2c008 System.Timers.ElapsedEventHandler 
0012F3BC 00c2bfb0 System.Timers.Timer 
0012F3C0 00c2bfb0 System.Timers.Timer 
0012F3C4 00c2bfb0 System.Timers.Timer 
0012F3C8 00c2bf50 System.Threading.Timer 
0012F3CC 00c2bfb0 System.Timers.Timer 
0012F3D0 00c2bfb0 System.Timers.Timer 
0012F3D4 00c2bf50 System.Threading.Timer 
0012F3D8 00c2bee4 System.Object[] (System.String[]) 
0012F4C4 00c2bee4 System.Object[] (System.String[]) 
0012F66C 00c2bee4 System.Object[] (System.String[]) 
0012F6A0 00c2bee4 System.Object[] (System.String[]) 

!gcroot -nostacks 00c2bf50 

!gcroot -nostacks 00c2c034 
DOMAIN(0015DC38):HANDLE(Strong):9911c0:Root: 00c2c05c(System.Threading._TimerCallback)-> 
    00c2bfe8(System.Threading.TimerCallback)-> 
    00c2bfb0(System.Timers.Timer)-> 
    00c2c034(System.Threading.Timer) 

!gchandles 
GC Handle Statistics: 
Strong Handles:  22 
Pinned Handles:  5 
Async Pinned Handles: 0 
Ref Count Handles: 0 
Weak Long Handles: 0 
Weak Short Handles: 0 
Other Handles:  0 
Statistics: 
     MT Count TotalSize Class Name 
7aa132b4  1   12 System.Diagnostics.TraceListenerCollection 
79b9f720  1   12 System.Object 
79ba1c50  1   28 System.SharedStatics 
79ba37a8  1   36 System.Security.PermissionSet 
79baa940  2   40 System.Threading._TimerCallback 
79b9ff20  1   84 System.ExecutionEngineException 
79b9fed4  1   84 System.StackOverflowException 
79b9fe88  1   84 System.OutOfMemoryException 
79b9fd44  1   84 System.Exception 
7aa131b0  2   96 System.Diagnostics.DefaultTraceListener 
79ba1000  1   112 System.AppDomain 
79ba0104  3   144 System.Threading.Thread 
79b9ff6c  2   168 System.Threading.ThreadAbortException 
79b56d60  9  17128 System.Object[] 
Total 27 objects 

के रूप में जॉन ने अपने जवाब में बताया, दोनों टाइमर रजिस्टर उनके GCHandle तालिका में कॉलबैक (System.Threading._TimerCallback)। जैसा कि हंस ने अपनी टिप्पणी में बताया, state पैरामीटर भी किया जाता है जब यह किया जाता है।

जॉन के रूप में कहा, कारण System.Timers.Timer जिंदा रखा जाता है क्योंकि यह कॉलबैक (यह भीतरी System.Threading.Timer करने के लिए state पैरामीटर के रूप में पारित हो जाता है) द्वारा संदर्भित है है; इसी तरह, कारण हमारे System.Threading.Timer जीसीएड है क्योंकि यह है जो इसके कॉलबैक द्वारा संदर्भित है।

timer1 के कॉलबैक (उदा। Console.WriteLine("Stayin alive (" + timer1.GetType().FullName + ")")) के लिए एक स्पष्ट संदर्भ जोड़ना जीसी को रोकने के लिए पर्याप्त है।

System.Threading.Timer पर सिंगल-पैरामीटर कन्स्ट्रक्टर का उपयोग करना भी काम करता है, क्योंकि टाइमर स्वयं को state पैरामीटर के रूप में संदर्भित करेगा। निम्नलिखित कोड, दोनों टाइमर जीसी के बाद जिंदा रहता है के बाद से वे एक GCHandle मेज से उनके कॉलबैक द्वारा संदर्भित कर रहे हैं:

class Program 
{ 
    static void Main(string[] args) 
    { 
    System.Threading.Timer timer1 = null; 
    timer1 = new System.Threading.Timer(_ => Console.WriteLine("Stayin alive (1)...")); 
    timer1.Change(0, 400); 

    var timer2 = new System.Timers.Timer 
    { 
     Interval = 400, 
     AutoReset = true 
    }; 
    timer2.Elapsed += (_, __) => Console.WriteLine("Stayin alive (2)..."); 
    timer2.Enabled = true; 

    System.Threading.Thread.Sleep(2000); 

    Console.WriteLine("Invoking GC.Collect..."); 
    GC.Collect(); 

    Console.ReadKey(); 
    } 
} 
+0

'टाइमर 1 'कचरा इकट्ठा क्यों किया जाता है? क्या यह अभी भी गुंजाइश में नहीं है? –

+3

जेफ: स्कोप वास्तव में प्रासंगिक नहीं है। जीसी.किपलाइव विधि के लिए यह काफी जहर-डीट्रे है। यदि आप picky विवरण में रूचि रखते हैं, तो http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx देखें। –

+7

टाइमर पर रिफ्लेक्टर के साथ एक नज़र डालें। सक्षम सेटटर। सिस्टम टाइमर को कॉलबैक में उपयोग करने के लिए सिस्टम ऑब्जेक्ट देने के लिए "कुकी" के साथ उपयोग की जाने वाली चाल को नोट करें। एसएससीआईएल 20 स्रोत कोड में सीएलआर इसके बारे में पता है, clr/src/vm/comthreadpool.cpp, CorCreateTimer()। MakeDelegateInfo() जटिल हो जाता है। –

उत्तर

26

आप windbg, एसओएस के साथ इस और इसी तरह के सवालों का जवाब कर सकते हैं और !gcroot

0:008> !gcroot -nostacks 0000000002354160 
DOMAIN(00000000002FE6A0):HANDLE(Strong):241320:Root:00000000023541a8(System.Thre 
ading._TimerCallback)-> 
00000000023540c8(System.Threading.TimerCallback)-> 
0000000002354050(System.Timers.Timer)-> 
0000000002354160(System.Threading.Timer) 
0:008> 

दोनों ही मामलों में, देशी टाइमर कॉलबैक वस्तु के जी सी (एक GCHandle के माध्यम से) को रोकने के लिए है। अंतर System.Timers.Timer के मामले में कॉलबैक संदर्भ System.Timers.Timer वस्तु (जो आंतरिक रूप से कार्यान्वित किया जाता है एक System.Threading.Timer का प्रयोग करके)

+4

अस्पष्ट टाइमर/जीसी ज्ञान और "woot" –

-3

आप

GC.KeepAlive(timer1); 

उपयोग कर सकते हैं इस वस्तु पर कचरा संग्रहण को रोकने के लिए।

+9

-1: इस तरह के गलत सवाल यहां जवाब देते हैं। वह पूछता है कि टाइमर जो जीवित रहता है * जीवित रहता है, न कि दूसरे को कैसे जीवित बनाते हैं। –

0

टाइमर 1 में आप इसे कॉलबैक दे रहे हैं। टाइमर 2 में आप एक ईवेंट हैंडलर को जोड़ रहे हैं; यह आपके प्रोग्राम क्लास का संदर्भ स्थापित करता है जिसका अर्थ है कि टाइमर जीसीड नहीं होगा। चूंकि आप कभी भी टाइमर 1 के मान का उपयोग नहीं करते हैं, (मूल रूप से वही है जैसे आपने var timer1 = को हटा दिया है) कंपाइलर चर को दूर करने के लिए पर्याप्त स्मार्ट है। जब आप जीसी कॉल को दबाते हैं, तो टाइमर 1 का संदर्भ नहीं लेता है, इसलिए इसे 'एकत्रित किया जाता है।

एक कंसोल जोड़ें। टाइमर 1 के गुणों में से किसी एक को आउटपुट करने के लिए अपने जीसी कॉल के बाद राइटलाइन और आपको पता चलेगा कि यह अब एकत्र नहीं हुआ है।

+3

इवेंट हैंडलर के पास 'प्रोग्राम' क्लास का संदर्भ नहीं है, और यहां तक ​​कि अगर ऐसा होता है, तो यह टाइमर को जीसीएड होने से नहीं रोकेगा। –

+1

हां यह करता है। उपरोक्त कोड संकलित करें और फिर इसे नेट परावर्तक के साथ देखें। + = लांबा को प्रोग्राम क्लास में एक विधि में परिवर्तित किया जाता है। और हां, ईवेंट हैंडलर जुड़े हुए हैं कचरा संग्रह को रोकें। http://blogs.msdn.com/b/abhinaba/archive/2009/05/05/memory-leak-via-event-handlers.aspx – Andy

6

मैं Task.Delay के कुछ उदाहरण कार्यान्वयन देख रही है और कुछ प्रयोग करने के बाद हाल ही में इस मुद्दे googling किया गया है।

यह पता चला है कि सिस्टम है या नहीं। थ्रेडिंग। टिमर जीसीडी इस पर निर्भर करता है कि आप इसे कैसे बनाते हैं !!!

यदि केवल कॉलबैक के साथ बनाया गया है तो राज्य वस्तु टाइमर ही होगी और यह इसे जीसीएडी होने से रोक देगा। यह कहीं भी दस्तावेज प्रतीत नहीं होता है और फिर भी इसके बिना आग बनाने और टाइमर भूलना बेहद मुश्किल है।

मैं http://www.dotnetframework.org/default.aspx/DotNET/DotNET/[email protected]/untmp/whidbey/REDBITS/ndp/clr/src/BCL/System/Threading/[email protected]/1/[email protected]

इस कोड में टिप्पणियां

पर कोड से पाया भी संकेत मिलता है कि ऐसा क्यों कॉलबैक संदर्भ देता है, तो टाइमर वस्तु अन्यथा के रूप में नई द्वारा वापस हो सकता है कॉलबैक-केवल ctor उपयोग करने के लिए हमेशा बेहतर है एक दौड़ बग।

0

एफवाईआई, .NET 4.6 (यदि पहले नहीं है) के रूप में, यह अब और सत्य नहीं प्रतीत होता है। आपका परीक्षण कार्यक्रम, आज चलने पर, नतीजतन टाइमर को कचरा नहीं मिलता है।

Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Invoking GC.Collect... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 
Stayin alive (2)... 
Stayin alive (1)... 

जैसा कि मैंने implementation of System.Threading.Timer को देखो, इस के रूप में यह प्रतीत होता है कि नेट के वर्तमान संस्करण सक्रिय टाइमर वस्तुओं की सूची से जुड़ा हुआ उपयोग करता है और कि लिंक्ड सूची TimerQueue अंदर एक सदस्य चर द्वारा आयोजित किया जाता है (समझ बनाने के लिए लगता है जो टाइमरक्यू में भी एक स्थिर सदस्य चर द्वारा जीवित रखा गया एक सिंगलटन ऑब्जेक्ट है)। नतीजतन, जब तक वे सक्रिय होते हैं, तब तक सभी टाइमर उदाहरण जीवित रहेंगे।

+1

के लिए +1 अभी भी 'System.hreading.Timer' उदाहरण को .NET 4.6 में एकत्रित किया जा रहा है। कृपया सुनिश्चित करें कि आप ऑप्टिमाइज़ेशन सक्षम के साथ रिलीज मोड में कोड संकलित कर रहे हैं। आपके द्वारा उल्लिखित लिंक्ड सूची में सहायक 'टाइमरक्यूयू टाइमर' ऑब्जेक्ट्स हैं; यह जीसी द्वारा एकत्रित होने से मूल 'सिस्टम। थ्रेडिंग। टिमर' उदाहरण को रोकता नहीं है। (प्रत्येक 'System.Threading.Timer' उदाहरण अपने स्वयं के 'टाइमरक्यूयू टाइमर' ऑब्जेक्ट का संदर्भ देता है, लेकिन इसके विपरीत नहीं। जब 'सिस्टम। थ्रेडिंग टिमर' जीसी द्वारा एकत्र किया जाता है, तो इसकी 'टाइमरक्यूयू टाइमर' ऑब्जेक्ट कतार से हटा दी जाती है '~ टाइमरहोल्डर 'फाइनलाइज़र।) – Antosha

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