Net资源泄露(内存泄露,GDI泄露,handle 泄露等)的终极解决方案

Net资源泄露(内存泄露,GDI泄露,handle 泄露等)的终极解决方案

      ——从内存泄露说起

提要

  内存泄露发生了怎么办?如何预防内存泄露的发生?我的经验是解决内存泄露的根本办法是编码时有预防意识。

  目录

1.内存泄露

  1.1怎样才算是发生了内存泄露

  1.2判断工具(perfmon.msc)

    1.2.1perfmon.msc的使用

     1.2.2一些重要的性能计数器

   1.3其他检测工具

2.如何在日常编程中预防内存泄露的发生

  2.1 Dispose()的使用

  2.2 using的使用

  2.3 事件的卸载

  2.4继承 IDisposable

    Net如何继承IDisposable接口,实现自己的Dispose()函数

4.其他资源泄露

   GDI leak,handle leak。

 5.几个特例

 6.参考文献

  (1)发现并防止托管代码中出现内存泄漏 ;

 (2)

正文

1.内存泄露

   刚开始使用Net的读者(甚至做了一两年商业开发的同行)可能对Net的内存泄露不是很了解,甚至会说Net不存在内存泄露,他们会问“不是有GC机制吗?”恩,是有这么回事,它保证了通常应用时不用考虑头疼的资源释放问题,但这个机制不保证你开发的程序就不存在内存泄露(在此我们假设程序不存在逻辑错误等Bug,下同)。

  同时, 一方面,GC机制本身的缺陷造成的,它做Collect操作时是基于一定的算法,虽然这个算法随着Net版本的升级在逐步的优化,但还是不能保证及时迅速准确地把Garbage回收(一定程度上也没必要,特别是随着机器硬件性能的大幅提升,犯不着这么做,怎么着GC也是需要开支的),这就需要在编码过程中通知那些资源可以被释放,特别是大对象,否则可能造成“泄露”;另一方面,Net中托管资源和非托管资源的处理是有差异的,托管资源的处理是由GC自动执行的,而非托管资源 (占少部分,比如文件操作,网络连接等)必须显式地释放,否则就可能造成泄露。综合起来说的话,由于托管资源在Net中占大多数,通常不做显式的资源释放是可以的,不会造成明显的资源泄露,而非托管资源则不然,是发生问题的主战场,是最需要注意的地方。

   另外,照我看来,很多情况下,衰老测试关注的主要是有没有内存泄露的发生,而对其他泄露的重视次之。为什么这样做呢,我认为有两方面的原因,一是内存跟其他资源是正相关的,也就是说没有内存泄露的发生,其他泄露的发生概率也较小,其根本在于所有的资源最后会反应在内存上;另一个就是很多Net应用开发,用到的非托管资源,多提供Dispose方法的,而Dispose之后,不光内存及时释放,其他的也做了释放。因此,通常情况下我们主要关注的是内存的使用情况,而对自定义控件开发等情况则需要关注GDI,handle等其他资源的情况。

1.1怎样才算是发生了内存泄露

  上面说了这么多,那么到底如何判断有没有内存泄露的发生呢?

  如果程序报“Out of memory”之类的错误,事实上也占据了很大部分的内存,应该说是典型的内存泄露,这种情况属于彻底的Bug,解决之道就是找到问题点,改正。但我的经验中,这种三下两下的就明显的泄露的情况较少,除非有人在很困的情况下编码,否则大多是隐性或渐进式地泄露,这种需经过较长时间的衰老测试才能发现,或者在特定条件下才出现,对这种情况要确定问题比较费劲,有一些工具(详见1.3)可以利用,但我总感觉效果一般,也可能是我不会使用吧,我想大型程序估计得无可奈何的用这个,详细的参见相关手册。

  需要强调的是,判断一个程序是不是出现了"memory leak",关键不是看它占用的内存有多大,而是放在一个足够长的时期(程序进入稳定运行状态后)内,看内存是不是还是一直往上涨,因此,刚开始的涨动或者前期的涨动不能做为泄露的充分证据。

  以上呢都是些感性的说法,具体的判断是否发生了内存泄露,可以通过一些性能计数器来测定。一般来讲,我测性能时,主要关注Process里 以下几个指标,如果这些量整体来看是持续上升的,基本可以判断是有泄露情况存在的。

A.Handle Count

B.Thread Count

C.Private Bytes

D.Virtual Bytes

E.Working Set

F.另外.NET CLR Memory下的Bytes in all heeps也是我比较关注的。

  通关观察,你如果发现这些参数是在一个区间内震荡的,应该是没有大的问题的,如果是一个持续上涨的状态,那你就得注意,很可能存在内存泄露。


1.2判断工具(perfmon.msc)

  如何测定以上的计数器呢,我大多使用windows自带的perfmon.msc。在此稍微说说改工具的使用。

    1.2.1perfmon.msc的使用

 

      由于现在不能贴图,以后补齐吧。在Run中输入perfmon.msc,运行,其他的自己摸索,不难。

     1.2.2一些重要的性能计数器

      参考1,

     参考2

1.3其他检测工具

  我用过的里面CLRProfiler 和dotTrace 还行(下载地址见链接)windeg也还行。不过坦白的说,准确定位比较费劲,最好还是按常规的该Dispose的加Dispose,也可以加GC.Collect()。

2.如何在日常编程中预防内存泄露的发生

  2.1 Dispose()的使用

     如果使用的对象提供Dispose()方法,那么当你使用完毕或在必要的地方(比如Exception)调用该方法,特别是对非托管对象,一定要加以调用,以达到防止泄露的目的。另外很多时候程序提供对Dispose()的扩展,比如Form,在这个扩展的Dispose方法中你可以把大对象的引用什么的在退出前释放。

  2.2 using的使用

   using除了引用Dll的功用外,还可以限制对象的适用范围,当超出这个界限后对象自动释放,比如

  2.3 事件的卸载

  2.4 API的调用

待续........

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yuanhuiqiao/archive/2010/01/28/5264480.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值