堆栈溢出和内存泄漏的区别 未释放事件Handler可能导致内存泄漏
未释放事件Handler可能导致内存泄漏
以前曾看见过这样一个问题 托管代码会不会导致内存泄漏 自己对GC的了解也不是很深 但还是比较赞成这样的观点 托管代码不会产生内存泄漏 除非你没有正确释放非托管资源
今天看到一个非常有趣的例子 关于没有释放事件的Handler导致的内存泄漏
以前对于释放Handler的观念是一点也没有 这主要因为没此方面的意识 没有养成好的习惯 只知道当关心这个事件的时候就注册一下 暂时不关心了就移除掉 却从来没有想到最终不移除不必要的Handler会导致此类无法被正常回收 导致不必要的内存浪费
事情是这样的 今天在看项目Source Code的时候发现一个有趣的字眼 WeakEvent 自己以前对WeakReference有点了解 所以就好奇地看看这是个啥玩意
发现其是一种通过弱引用实现的Delegate 因为没有太多的注释 所有不知其为啥用此种方式来封装事件 于是顺手Google了一下 找到了一篇关于weak event的非常有意思的文章
文章里提出了一个问题 场景如下
UnRelease Event Handler
using System
using System Collections Generic
using System Text
using Microsoft Win
namespace ConsoleApplication
{
class DisplaySettingsListener
{
byte[] m_ExtraMemory = new byte[ ]
public DisplaySettingsListener()
{
SystemEvents DisplaySettingsChanged += new EventHandler(ehDisplaySettingsChanged)
}
private void ehDisplaySettingsChanged(object sender EventArgs e)
{
}
}
class Program
{
static void DisplayMemory()
{
Console WriteLine( Total memory { ### ### ### ## } bytes GC GetTotalMemory(true))
}
static void Main()
{
DisplayMemory()
Console WriteLine()
for (int i = i < i++)
{
Console WriteLine( —— New Listener #{ } —— i + )
DisplaySettingsListener listener = new DisplaySettingsListener()
listener = null
GC Collect()
DisplayMemory()
}
Console Read()
}
}
}
运行的结果如下
虽然我们释放了对listener的引用 并且强制GC进行回收 但我们可以看到其内存占用量还是变大了 出乎了我的意料
这就是该文作者指出的事件列表里保存的是一个强命名的引用而非弱引用 虽然上面释放了listener变量对Listener实例的引用 但因为仍然在DisplaySettingsChanged事件列表里保存了对Listener实例的引用 导致Listener实例并不能被垃圾回收(有人引用 自然不会回收)
那么接下来看看下面的代码
Release Event Hanlder
class DisplaySettingsListener IDisposable
{
byte[] m_ExtraMemory = new byte[ ]
public DisplaySettingsListener()
{
SystemEvents DisplaySettingsChanged += new EventHandler(ehDisplaySettingsChanged)
}
private void ehDisplaySettingsChanged(object sender EventArgs e)
{
}
IDisposable Members#region IDisposable Members
public void Dispose()
{
SystemEvents DisplaySettingsChanged = new EventHandler(ehDisplaySettingsChanged)
}
#endregion
}
class Program
{
static void DisplayMemory()

{
Console WriteLine( Total memory { ### ### ### ## } bytes GC GetTotalMemory(true))
}
static void Main()
{
DisplayMemory()
Console WriteLine()
for (int i = i < i++)
{
Console WriteLine( —— New Listener #{ } —— i + )
DisplaySettingsListener listener = new DisplaySettingsListener()
listener Dispose()
listener = null
GC Collect()
DisplayMemory()
}
Console Read()
}
}
运行结果如下
结果是不是正如您猜测的呢 ) 已经成功地回收了listener实例 不知为何从 字节变到 字节 哪位高手赐教一下啊 )
详情可以看原文 The Problem With Delegates
在后续的文章中作者类似文章开头提到的Weak Event来解决这个问题 Solving the Problem with Events Weak Event Handlers
lishixinzhi/Article/program/net/201311/11737