相关文章推荐
刚毅的炒面  ·  C# 定时器保活机制引起的内存泄露问题开发者社区·  1 周前    · 
完美的脸盆  ·  C# ...·  1 周前    · 
无邪的黄花菜  ·  Disabling the MD5 ...·  1 年前    · 
睿智的充值卡  ·  “如何解决类型为‘unknown’的对象?”·  1 年前    · 
刚分手的钱包  ·  RabbitMQ学习第一记:用java连接R ...·  2 年前    · 
打酱油的帽子  ·  开发者中心_API调用说明 - 友盟+·  2 年前    · 
火星上的小熊猫  ·  fetch异步变同步-掘金·  2 年前    · 
Code  ›  C# 定时器保活机制引起的内存泄露问题开发者社区
system 定时器
https://cloud.tencent.com/developer/article/1584313
刚毅的炒面
1 周前
丹枫无迹

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

腾讯云
开发者社区
文档 建议反馈 控制台
首页
学习
活动
专区
圈层
工具
MCP广场
文章/答案/技术大牛
发布
丹枫无迹
社区首页 > 专栏 > C# 定时器保活机制引起的内存泄露问题

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

作者头像
丹枫无迹
发布 于 2020-02-17 10:18:04
发布 于 2020-02-17 10:18:04
1.9K 0
举报
文章被收录于专栏: 学无止境 学无止境

C# 中有三种定时器, System.Windows.Forms 中的定时器和 System.Timers.Timer 的工作方式是完全一样的,所以,这里我们仅讨论 System.Timers.Timer 和 System.Threading.Timer

1、定时器保活

先来看一个例子:

代码语言: javascript
复制
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 ----------");
}

运行结果如下:

代码语言: javascript
复制
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 。

代码语言: javascript
复制
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 方法改成 静态 的就好了。(静态方法属于类而非实例。)

改成静态方法后再次运行示例,结果如下:

代码语言: javascript
复制
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 ,又会如何?

代码语言: javascript
复制
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 ----------");
 
推荐文章
刚毅的炒面  ·  C# 定时器保活机制引起的内存泄露问题开发者社区
1 周前
完美的脸盆  ·  C# system.Threading.Timer和system.Timers中的定时器 - JohnYang819
1 周前
无邪的黄花菜  ·  Disabling the MD5 algorithm for IBM Java used by SiteProtector
1 年前
睿智的充值卡  ·  “如何解决类型为‘unknown’的对象?”
1 年前
刚分手的钱包  ·  RabbitMQ学习第一记:用java连接RabbitMQ - freeTimeWY - 博客园
2 年前
打酱油的帽子  ·  开发者中心_API调用说明 - 友盟+
2 年前
火星上的小熊猫  ·  fetch异步变同步-掘金
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号