相关文章推荐
任性的小熊猫  ·  os.environ['mapreduce_ ...·  4 月前    · 
逆袭的红酒  ·  LaTeX ...·  10 月前    · 
阳光的青蛙  ·  Spring data ...·  1 年前    · 

C# 定时器保活机制引起的内存泄露问题解决

作者:丹枫无迹

这篇文章主要介绍了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# 异步多线程入门到精通之Thread篇
    C# 异步多线程入门到精通之Thread篇
    2021-11-11
  • C#中CheckedListBox控件的用法实例
    C#中CheckedListBox控件的用法实例
    2021-11-11
  • 基于C# wpf 实现Grid内控件拖动详情
    基于C# wpf 实现Grid内控件拖动详情
    2021-11-11
  • C# 基于TCP 实现扫描指定ip端口的方式示例
    C# 基于TCP 实现扫描指定ip端口的方式示例
    2021-11-11
  • Unity 制作一个分数统计系统
    Unity 制作一个分数统计系统
    2021-11-11
  • C# StackExchange.Redis 用法汇总
    C# StackExchange.Redis 用法汇总
    2021-11-11
  • 基于C#实现端口扫描器(单线程和多线程)
    基于C#实现端口扫描器(单线程和多线程)
    2021-11-11
  • C# Quartzs定时器的使用教程
    C# Quartzs定时器的使用教程
    2021-11-11
  • 美国设下计谋,用娘炮文化重塑日本,已影响至中国
    美国设下计谋,用娘炮文化重塑日本,已影响至中国
    2021-11-19
  • 时空伴随者是什么意思?时空伴随者介绍
    时空伴随者是什么意思?时空伴随者介绍
    2021-11-09
  • 工信部称网盘企业免费用户最低速率应满足基本下载需求,天翼云盘回应:坚决支持,始终
    工信部称网盘企业免费用户最低速率应满足基本下载需求,天翼云盘回应:坚决支持,始终
    2021-11-05
  • 2022年放假安排出炉:五一连休5天 2022年所有节日一览表
    2022年放假安排出炉:五一连休5天 2022年所有节日一览表
    2021-10-26
  • 电脑版 - 返回首页

    2006-2023 脚本之家 JB51.Net , All Rights Reserved.
    苏ICP备14036222号