内存泄露发生了怎么办?如何预防内存泄露的发生?我的经验是解决内存泄露的根本办法是编码时有预防意识。
目录
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的调用
本文转自94cool博客园博客,原文链接:http://www.cnblogs.com/94cool/archive/2010/03/25/1695313.html,如需转载请自行联系原作者