您知道吗:未释放事件Handler可能导致内存泄漏

摘要:
以前曾看见过这样一个问题:托管代码会不会导致内存泄漏。今天看到一个非常有趣的例子,关于没有释放事件的Handler导致的内存泄漏。以前对于释放Handler的观念是一点也没有,这主要因为没此方面的意识,没有养成好的习惯。却从来没有想到最终不移除不必要的Handler会导致此类无法被正常回收,导致不必要的内存浪费。已经成功地回收了listener实例。

以前曾看见过这样一个问题:托管代码会不会导致内存泄漏。自己对GC的了解也不是很深,但还是比较赞成这样的观点:托管代码不会产生内存泄漏,除非你没有正确释放非托管资源。
今天看到一个非常有趣的例子,关于没有释放事件的Handler导致的内存泄漏。
以前对于释放Handler的观念是一点也没有,这主要因为没此方面的意识,没有养成好的习惯。只知道当关心这个事件的时候就注册一下, 暂时不关心了就移除掉。却从来没有想到最终不移除不必要的Handler会导致此类无法被正常回收,导致不必要的内存浪费。

事情是这样的,今天在看项目Source Code的时候发现一个有趣的字眼:"WeakEvent". 自己以前对WeakReference有点了解,所以就好奇地看看这是个啥玩意。
发现其是一种通过弱引用实现的Delegate。因为没有太多的注释,所有不知其为啥用此种方式来封装事件。于是顺手Google了一下,找到了一篇关于weak event的非常有意思的文章。
文章里提出了一个问题,场景如下:

您知道吗:未释放事件Handler可能导致内存泄漏第1张您知道吗:未释放事件Handler可能导致内存泄漏第2张UnRelease Event Handler
您知道吗:未释放事件Handler可能导致内存泄漏第3张usingSystem;
您知道吗:未释放事件Handler可能导致内存泄漏第3张
usingSystem.Collections.Generic;
您知道吗:未释放事件Handler可能导致内存泄漏第3张
usingSystem.Text;
您知道吗:未释放事件Handler可能导致内存泄漏第3张
usingMicrosoft.Win32;
您知道吗:未释放事件Handler可能导致内存泄漏第3张
您知道吗:未释放事件Handler可能导致内存泄漏第3张
namespaceConsoleApplication16
您知道吗:未释放事件Handler可能导致内存泄漏第9张您知道吗:未释放事件Handler可能导致内存泄漏第10张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张
classDisplaySettingsListener
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张
byte[]m_ExtraMemory=newbyte[1000000];
您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
publicDisplaySettingsListener()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张SystemEvents.DisplaySettingsChanged
+=newEventHandler(ehDisplaySettingsChanged);
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
privatevoidehDisplaySettingsChanged(objectsender,EventArgse)
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
classProgram
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张
staticvoidDisplayMemory()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.WriteLine(
"Totalmemory:{0:###,###,###,##0}bytes",GC.GetTotalMemory(true));
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
staticvoidMain()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张DisplayMemory();
您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.WriteLine();
您知道吗:未释放事件Handler可能导致内存泄漏第12张
for(inti=0;i<5;i++)
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.WriteLine(
"---NewListener#{0}---",i+1);
您知道吗:未释放事件Handler可能导致内存泄漏第12张DisplaySettingsListenerlistener
=newDisplaySettingsListener();
您知道吗:未释放事件Handler可能导致内存泄漏第12张listener
=null;
您知道吗:未释放事件Handler可能导致内存泄漏第12张GC.Collect();
您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张DisplayMemory();
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.Read();
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第64张}

运行的结果如下:

image

虽然我们释放了对listener的引用,并且强制GC进行回收,但我们可以看到其内存占用量还是变大了,出乎了我的意料。
这就是该文作者指出的事件列表里保存的是一个强引用而非弱引用。虽然上面释放了listener变量对Listener实例的引用,但因为仍然在DisplaySettingsChanged事件列表里保存了对Listener实例的引用,导致Listener实例并不能被垃圾回收(有人引用,自然不会回收)。
那么接下来看看下面的代码:

您知道吗:未释放事件Handler可能导致内存泄漏第66张您知道吗:未释放事件Handler可能导致内存泄漏第67张Release Event Hanlder
您知道吗:未释放事件Handler可能导致内存泄漏第3张classDisplaySettingsListener:IDisposable
您知道吗:未释放事件Handler可能导致内存泄漏第9张您知道吗:未释放事件Handler可能导致内存泄漏第10张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张
byte[]m_ExtraMemory=newbyte[1000000];
您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
publicDisplaySettingsListener()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张SystemEvents.DisplaySettingsChanged
+=newEventHandler(ehDisplaySettingsChanged);
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
privatevoidehDisplaySettingsChanged(objectsender,EventArgse)
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第87张您知道吗:未释放事件Handler可能导致内存泄漏第88张
IDisposableMembers#regionIDisposableMembers
您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
publicvoidDispose()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张SystemEvents.DisplaySettingsChanged
-=newEventHandler(ehDisplaySettingsChanged);
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第23张
#endregion

您知道吗:未释放事件Handler可能导致内存泄漏第64张}

您知道吗:未释放事件Handler可能导致内存泄漏第3张
您知道吗:未释放事件Handler可能导致内存泄漏第3张
classProgram
您知道吗:未释放事件Handler可能导致内存泄漏第9张您知道吗:未释放事件Handler可能导致内存泄漏第10张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张
staticvoidDisplayMemory()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.WriteLine(
"Totalmemory:{0:###,###,###,##0}bytes",GC.GetTotalMemory(true));
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张
staticvoidMain()
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张DisplayMemory();
您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.WriteLine();
您知道吗:未释放事件Handler可能导致内存泄漏第12张
for(inti=0;i<5;i++)
您知道吗:未释放事件Handler可能导致内存泄漏第13张您知道吗:未释放事件Handler可能导致内存泄漏第14张
您知道吗:未释放事件Handler可能导致内存泄漏第11张{
您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.WriteLine(
"---NewListener#{0}---",i+1);
您知道吗:未释放事件Handler可能导致内存泄漏第12张DisplaySettingsListenerlistener
=newDisplaySettingsListener();
您知道吗:未释放事件Handler可能导致内存泄漏第12张listener.Dispose();
您知道吗:未释放事件Handler可能导致内存泄漏第12张listener
=null;
您知道吗:未释放事件Handler可能导致内存泄漏第12张GC.Collect();
您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第12张DisplayMemory();
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张Console.Read();
您知道吗:未释放事件Handler可能导致内存泄漏第23张}

您知道吗:未释放事件Handler可能导致内存泄漏第12张
您知道吗:未释放事件Handler可能导致内存泄漏第64张}

运行结果如下:

image
结果是不是正如您猜测的呢:)。已经成功地回收了listener实例。 不知为何从432944字节变到446980字节,哪位高手赐教一下啊:)
详情可以看原文 The Problem With Delegates
在后续的文章中作者类似文章开头提到的Weak Event来解决这个问题: Solving the Problem with Events: Weak Event Handlers

也许您觉得写这样的一个Weak Event没有必要或者显得麻烦,但您一定要记得及时地在必要的地方调用 -= 取消不再关心的事件。本文的目的也只是在此方面提个善意的提醒。

免责声明:文章转载自《您知道吗:未释放事件Handler可能导致内存泄漏》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇数据字典 dba_free_space及相对文件号RELATIVE_FNO 小结python学习笔记(十七)flask模块写接口下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

关于springMVC

一 mvc设计模式 MVC 全名是 Model View Controller,是 模型(model)-视图(view)-控制器(controller) 的缩写, 是⼀种⽤于设计创建 Web 应⽤程序表现层的模式。 MVC 中每个部分各司其职:Model(模型):模型包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。View(视图): 通...

Python——pyHook监听鼠标键盘事件

pyHook包为Windows中的全局鼠标和键盘事件提供回调。底层C库报告的信息包括事件的时间,事件发生的窗口名称,事件的值,任何键盘修饰符等。而正常工作需要pythoncom等操作系统的API的支持。 先链上一篇《pyHook和pythoncom的安装》。 然后开始讲pyHook的使用。 关于pyHook的使用,网上现存的大多教程如下: import p...

nginx upstream模块

upstream模块 upstream模块 (100%) nginx模块一般被分成三大类:handler、filter和upstream。前面的章节中,读者已经了解了handler、filter。 利用这两类模块,可以使nginx轻松完成任何单机工作。而本章介绍的upstream,将使nginx将跨越单机的限制,完成网络数据的接收、处理和转 发。 数据转...

hadoop26----netty,多个handler

k客户端: package cn.itcast_03_netty.sendorder.client; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; im...

element ui 的 element-tree文字显示不全的问题

在elemtn-tree 树展示的时候外面设置了固定宽度,超出的文字会隐藏,认真查看过各个嵌套节点并没有发现超出隐藏的设置。这就比较尴尬。  解决方案一、给超出的文件加上滑块(缺点不够美观,如果连续都超出的话就看着比较费劲) element 树部分的代码! <el-tree     :data="data" show-...

android 在子线程中使用handler更新界面

1. 在子线程中创建一个handler对象,让这个handler对象获取主线程的looper,这样才能把这个handler中的消息发送到ui线程的消息队列中 下面这个界面当点击updateui按钮就会创建一个对象然后调用它的更新图片和文字的方法,这两个设置方法在子线程中执行。 在更新界面的对象的类中创建一个handler对象,在初始化的时候给他赋值为Lo...