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# 定时器导致的内存泄露问题的更多相关文章
- dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露
本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...
- logging 模块误用导致的内存泄露
首先介绍下怎么发现的吧, 线上的项目日志是通过 logging 模块打到 syslog 里, 跑了一段时间后发现 syslog 的 UDP 连接超过了 8W, 没错是 8 W. 主要是 logging ...
- 深度:ARC会导致的内存泄露
iOS提供了ARC功能,很大程度上简化了内存管理的代码. 但使用ARC并不代表了不会发生内存泄露,使用不当照样会发生内存泄露. 下面列举两种内存泄露的情况. 1,循环参照 A有个属性参照B,B有个属性 ...
- 可能会导致.NET内存泄露的8种行为
原文连接:https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/作者 Michael Shpilt.授权翻译,转载请保 ...
- JavaScript之详述闭包导致的内存泄露
一.内存泄露 1. 定义:一块被分配的内存既不能使用,也不能回收.从而影响性能,甚至导致程序崩溃. 2. 起因:JavaScript的垃圾自动回收机制会按一定的策略找出那些不再继续使用的变量,释放其占 ...
- 避免使用CreateThread函数,导致的内存泄露
原文链接:http://blog.csdn.net/solosure/article/details/6262877
- Android中Handler导致的内存泄露
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html Consider the follo ...
- Andorid 内存溢出与内存泄露,几种常见导致内存泄露的写法
内存泄露,大部分是因为程序的逻辑不严谨,但是又可以跑通顺,然后导致的,内存溢出不会报错,如果不看日志信息是并不知道有泄露的.但是如果一直泄露,然后最终导致的内存溢出,仍然会使程序挂掉.内存溢出大部分是 ...
- 在iOS上自动检测内存泄露
手机设备的内存是一个共享资源.应用程序可能会不当的耗尽内存.崩溃,或者遭遇大幅度的性能降低. Facebook iOS客户端有很多功能,并且它们共享同一块内存空间.如果任何特定的功能消耗过多的内存,就 ...
随机推荐
- JVM 体系结构与工作方式
.katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...
- [梁山好汉说IT] 以水浒传为例讲解贝叶斯定理
0x00 摘要 看看呼延灼如何利用贝叶斯定理来判断 "自己是否是公明哥哥的心腹". 0x01 IT概念 1. 贝叶斯定理 贝叶斯定理是用来解决"逆概率"问题的, ...
- AndroidStudio插件大全
Android-Studio-Plugins 原文地址:https://github.com/itgoyo/Android-Studio-Plugins 欢迎star~~~ Android-Studi ...
- mac系统Docker安装Redis教程
在之前介绍过mac系统安装docker,以及docker安装mysql的傻瓜式教程,如果有需要参考的朋友,可以看下面文章: MacOS安装Docker傻瓜式教程 mac系统下docker安装配置mys ...
- [Oracle]Oracle的闪回归档
Oracle的闪回归档 场景需求,由于管理数据库的一些核心表,在实施初期会有人为误删除的问题.Oracle 11gR2提供了闪回归档的特性,可以保证不用RMAN来恢复误删除的数据.实践如下: 1.创建 ...
- 「2015南阳CCPC D」金砖 解题报告
金砖 Problem 有一个长度为L的板凳,可以放一排金砖,金砖不能重叠.特别的,摆放的金砖可以超出板凳,前提是必须保证该金砖不会掉下去,即该金砖的重心必须在板凳上. 每块金砖都一个长度和价值,且金砖 ...
- .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析
介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...
- html1,初识html
vs code编辑器 安装插件 chinese 中文支持 open in browser 快速预览文件 view in browser 快捷键 快捷键 描述 shift + end 选中从光标到行尾 ...
- 【转】Vim显示中文乱码
Windows下,在Vim中如果想让中文正常显示,可以在 Vim安装目录下找到_vimrc 文件,用记事本打开就行,然后在其中加入如下语句: set fileencodings=gb2312,gb ...
- input 只允许输入小数
oninput = "value=value.replace(/[^\d]/g,'')" 输入浮点数不好使 突发奇想自己写一个与众不同的... oninput="valu ...