MyException - 我的异常网
当前位置:我的异常网» .NET相关 » 【.NET深呼吸】清理对象引用,有一个有关问题容易被

【.NET深呼吸】清理对象引用,有一个有关问题容易被忽略

www.MyException.Cn  网友分享于:2015-02-11  浏览:0次
【.NET深呼吸】清理对象引用,有一个问题容易被忽略

大家知道,托管代码一个重要的特点是自动管理内存,即我们常说的垃圾回收机制,那些高大上的理论我就不重复了,有兴趣的朋友可以翻书。我这个有个毛病——不喜欢很严肃地去说一些理论的东西,所以我不多介绍了。

一般而言,当代码执行超出某个变量的有效范围后,或者不再引用某个对象实例时,该实例会发生析构,垃圾回收器很可能就要清理门户了,当然也可能不是马上清理,也许会过一会儿再清理。

对于一些要自定义进行清理操作的类,我们会采取以下方案:

1、写上析构函数,在析构函数中清理。

2、实现IDisposable接口,并实现Dispose方法,在方法中编写自定义清理代码。当该类型被实例化后,最后不再使用时会调用Dispose方法清理,如果顺利清理,最后还会调用类型的析构函数。通常,如何实现了IDisposable接口,就不必再写上析构函数了。如果希望Dispose方法被自动调用,可以在实例化对象的代码包装在using语句块中,当执行完using块时会自动调用Dispose方法。

 

可能有人笑了,老周,你太逗了,这些基础知识谁不知道?当然,我说上面那些内容是为了绕个小圈子,以便进入主题。于是,我产生了一个疑问:是不是存在某些情景下,可能导致对象实例不会被回收呢?就算你调用了Dispose方法,就算你把变量设为null来解除引用,就算你调用GC类的方法来回收……

经过老周测试,还真有这种情况,而且很多朋友都很有可能会忽略,甚至在意识认知上误认为对象实例已经被回收,而实际上是没有回收的。

 

我简单说一下这种情形:

比如有一个静态类(静态类的成员必是静态的)A,里面有静态事件。随后在其他类的实例中处理A类的静态事件,并且处理事件的方法就位于这个实例对象上……

不急,我们还是看真实的例子吧。假如我定义了一个静态类MyChecker,它里面有个静态事件CheckEvent。

    public static class MyChecker
    {
        #region 静态事件
        public static event EventHandler CheckEvent;
        #endregion

        public static void CallEvent()
        {
            if (CheckEvent != null)
            {
                CheckEvent(new object(), EventArgs.Empty);
            }
        }
    }

只要CallEvent方法被调用,CheckEvent事件会被引发。

 

然后,定义另一个类SampleClass,并在该类中处理刚才MyChecker中的静态事件。

    class SampleClass:IDisposable
    {
        public SampleClass()
        {
            MyChecker.CheckEvent += MyChecker_CheckEvent;
        }

        void MyChecker_CheckEvent(object sender, EventArgs e)
        {
            new Form2().Show();
        }

        ~SampleClass()
        {
            System.Diagnostics.Debug.WriteLine("\n看,析构函数调用了。\n");
        }

        public void Dispose()
        {
            //……
        }
    }


在类的构造函数中,附加CheckEvent事件的处理,处理方法名为MyChecker_CheckEvent。

可能大家已经发现,老周写的SampleClass类有点恐怖气息,既实现了Dispose方法,怎么又写了析构函数,我这里写上析构函数是为了验证类的实例是否真的被清理,如果实例真的被回收,那么Debug类会在“输出”窗口中输出提示,如果没有提示输出,说明类的实例还霸占着内存。

接下来测试一下。

            SampleClass sc = new SampleClass();
            await Task.Delay(10 * 1000);
            sc.Dispose();
            sc = null;
            GC.Collect();


实例化SampleClass后,然后Delay会暂停10秒,10秒钟过后会调用Dispose方法,并设置变量为null引用,我害怕不能及时清理,连GC.Collect方法也用上了。

而在等待这10秒期间,可以调用静态类的CallEvent方法来引发静态事件CheckEvent。

MyChecker.CallEvent();

按照一般理解,在10秒钟后,SampleClass实例应该被清理,并且在“输出”窗口会输出提示。

好,现在试一下。

……

实验结果表明,输出 窗口中连鸭毛都没有输出,这说明10秒钟后,SampleClass实例根本没有发生析构。于是又出问题了,这是怎么回事?SampleClass实例不是不存在引用了吗,怎么不发生析构?

其实我们忽略了一点:静态事件CheckEvent还跟SampleClass实例的方法绑定着呢,实质上,虽然将变量设为null,可是SampleClass实例中的MyChecker_CheckEvent方法还被静态类中的静态事件引用着,所以不会被回收。不知道你明白了没。

这个问题很多朋友在实际开发中都会忽略,还得意地以为Sample实例真的被回收了,实际上实例不会被回收,除非程序结束。因为MyChecker是静态类,不基于实例。如果MyChecker不是静态类,那么当MyChecker的实例释放后,SampleClass实例就可以被释放了。

那么,如何解决呢?很简单,只要在SampleClass类的Dispose方法中解除静态事件与方法的绑定即可,这样的话,静态事件就不再引用实例中的方法成员了,此时实例就可以发生析构了。

        public void Dispose()
        {
            MyChecker.CheckEvent -= MyChecker_CheckEvent;
        }

 

这个例子研究,告诉我们:在类实例中处理静态事件时一定要小心

本示例的源码下载:http://files.cnblogs.com/files/tcjiaan/refsample.zip

好了,今天就扯到这里吧。

 

文章评论

为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
漫画:程序员的工作
漫画:程序员的工作
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
总结2014中国互联网十大段子
总结2014中国互联网十大段子
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
旅行,写作,编程
旅行,写作,编程
编程语言是女人
编程语言是女人
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
如何成为一名黑客
如何成为一名黑客
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
程序员都该阅读的书
程序员都该阅读的书
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员必看的十大电影
程序员必看的十大电影
为什么程序员都是夜猫子
为什么程序员都是夜猫子
那些争议最大的编程观点
那些争议最大的编程观点
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
Java程序员必看电影
Java程序员必看电影
鲜为人知的编程真相
鲜为人知的编程真相
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
每天工作4小时的程序员
每天工作4小时的程序员
10个调试和排错的小建议
10个调试和排错的小建议
 程序员的样子
程序员的样子
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
代码女神横空出世
代码女神横空出世
程序员和编码员之间的区别
程序员和编码员之间的区别
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
程序员的鄙视链
程序员的鄙视链
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
我是如何打败拖延症的
我是如何打败拖延症的
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
老程序员的下场
老程序员的下场
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有