您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何解決C#定時器保活機制引起的內存泄露問題,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
C# 中有三種定時器,System.Windows.Forms 中的定時器和 System.Timers.Timer 的工作方式是完全一樣的,所以,這里我們僅討論 System.Timers.Timer 和 System.Threading.Timer
1、定時器保活
先來看一個例子:
class Program{ static void Main(string[] args) { Start(); GC.Collect(); Read(); } static void Start() { Foo f = new Foo(); System.Threading.Thread.Sleep(5_000); }}public class Foo{ System.Timers.Timer _timer; public Foo() { _timer = new System.Timers.Timer(1000); _timer.Elapsed += timer_Elapsed; _timer.Start(); } private void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { WriteLine("System.Timers.Timer Elapsed."); } ~Foo() { WriteLine("---------- End ----------"); }}
運行結果如下:
System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed....
在 Start 方法結束后,Foo 實例已經失去了作用域,按理說應該被回收,但實際并沒有(因為析構函數沒有執行,所以肯定實例未被回收)。
這就是定時器的 保活機制,因為定時器需要執行 timer_Elapsed 方法,而該方法屬于 Foo 實例,所以 Foo 實例被保活了。
但多數時候這并不是我們想要的結果,這種結果導致的結果就是 內存泄露,解決方案是:先將定時器 Dispose。
public class Foo : IDisposable{ ... public void Dispose() { _timer.Dispose(); }}
一個很好的準則是:如果類中的任何字段所賦的對象實現了IDisposable 接口,那么該類也應當實現 IDisposable 接口。
在這個例子中,不止 Dispose 方法,Stop 方法和設置 AutoReset = false,都能起到釋放對象的目的。但是如果在 Stop 方法之后又調用了 Start 方法,那么對象依然會被保活,即便 Stop 之后進行強制垃圾回收,也無法回收對象。
System.Timers.Timer
和 System.Threading.Timer
的保活機制是類似的。
保活機制是由于定時器引用了實例中的方法,那么,如果定時器不引用實例中的方法呢?
2、不保活下 System.Timers.Timer 和 System.Threading.Timer 的差異
要消除定時器對實例方法的引用也很簡單,將 timer_Elapsed 方法改成 靜態 的就好了。(靜態方法屬于類而非實例。)
改成靜態方法后再次運行示例,結果如下:
System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.---------- End ----------System.Timers.Timer Elapsed.System.Timers.Timer Elapsed.System.Timers.Timer Elapsed....
Foo 實例是被銷毀了(析構函數已運行,打印出了 End),但定時器還在執行,這是為什么呢?
這是因為,.NET Framework 會確保 System.Timers.Timer 的存活,即便其所屬實例已經被銷毀回收。
如果改成 System.Threading.Timer,又會如何?
class Program{ static void Main(string[] args) { Start(); GC.Collect(); Read(); } static void Start() { Foo2 f2 = new Foo2(); System.Threading.Thread.Sleep(5_000); }}public class Foo2{ System.Threading.Timer _timer; public Foo2() { _timer = new System.Threading.Timer(timerTick, null, 0, 1000); } static void timerTick(object state) { WriteLine("System.Threading.Timer Elapsed."); } ~Foo2() { WriteLine("---------- End ----------"); }}
注意,這里的 timerTick 方法是靜態的。運行結果如下:
System.Threading.Timer Elapsed.System.Threading.Timer Elapsed.System.Threading.Timer Elapsed.System.Threading.Timer Elapsed.System.Threading.Timer Elapsed.---------- End ----------
可見,隨著 Foo2 實例銷毀,_timer 也自動停止并銷毀了。
這是因為,.NET Framework 不會保存激活 System.Threading.Timer 的引用,而是直接引用回調委托。
關于如何解決C#定時器保活機制引起的內存泄露問題就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。