分享.net常见的内存泄露及解决方法
2013-07-04 16:50
232 查看
分享.net常见的内存泄露及解决方法
关于内存泄漏的问题,之前也为大家介绍过,比如:《C++中内存泄漏的检测方法介绍》,是关于C++内存泄漏的。今天为大家介绍的是关于.NET内存泄漏的问题。前段时间帮项目组内做了一次内存优化,产品是使用c#开发的winForm程序,一直以为.net提供了垃圾收集机制,开发的时候也没怎么注意内存的释放,导致最后的产品做出来之后,运行一个多小时就内存直接崩溃了,看来.net的垃圾收集还是得需要开发者加以控制,也不是万能的啊。
下面将对垃圾收集做以简介,然后描述一下我在内存优化过程中常见的内存泄露及解决方法。
托管堆的内存分配(下文中的托管堆指的是GC堆)
托管堆是以应用程序域为依托的,即每一个应用程序域有一个托管堆,每一个托管堆也只属于一个应用程序域,且托管堆是一块连续的内存,其中的对象也是紧密排列的。相对于C++中的非连续内存堆来说,托管堆的内存分配效率要高。托管堆维护了一个指针,指向当前已使用内存的末尾,当需要分配内存的时候,只需要指针向后移动指定数量的位置即可。而且托管堆通过应用程序域实现了应用程序之间内存的隔离,即不同的应用程序域之间在正常情况下是不能相互访问各自的托管堆的。
垃圾收集
垃圾收集的算法有很多。例如引用计数、标记清除等等,托管堆使用的标记清除算法。
托管堆使用的是分代标记清除算法。
标记清除算法
首先,系统将托管堆内所有的对象视为可以回收的垃圾,然后系统从GCRoot开始遍历托管堆内所有的对象,将遍历到的对象标记为可达对象,在遍历完成之后,回收所有的非可达对象,完成一遍垃圾收集。
注意,托管堆的垃圾收集只会自己收集托管对象!
由于在执行完垃圾收集之后,托管堆中会产生很多的内存碎片,导致内存不再连续,因此在垃圾收集完成之后,系统会执行一次内存压缩,将不连续的内存重新排列整齐,变成连续的内存。(关于垃圾收集的详细信息,大家可以参考《CLR Via C#》)
通过上面的简述,大家都知道什么样的对象不会被收集,即能从GCRoot开始遍历到的对象。
最常见的GCRoot是线程的栈,线程的栈里面通常包含方法的参数、临时变量等。另外常见的GCRoot还有静态字段、CPU寄存器以及LOH堆上的大的集合。因此,如果想要让托管对象的内存顺利的释放,只需要断开与跟之间的联系即可。而对于非托管对象的内存,必须进行手动释放。
下面我根据自己在优化内存的过场中的一些常见错误以及一些解决方法。
事件
在.net内存泄露的原因当中,事件占据了非常大的一部分比例,事件是一种委托的实例,也就是与我们类中其他的字段一样,也是一个字段。
委托为什么能阻止垃圾收集呢?即委托是如何让相关的对象在垃圾收集的时候被标记为可达对象的呢?首先要从委托的本质看起,
我们通常使用的委托是从类
public abstract class MulticastDelegate : Delegate |
internal object _target; internal object _methodBase; |
Code: public event EventHandler TestEvent; void MethodEndTempVarClear() { Test tempTestEvent = new Test(); TestEvent += tempTestEvent.TestEvent; } |
由上图我们可以看出,原本应该在方法结束后就可以变为不可达对象的tempTestEvent变成了可达对象,因此也不能对其进行收集了。
个人建议:将类中所有的事件订阅添加到一个专门的方法当中,且实现一个与其匹配的取消订阅的方法,并在必要的时候,调用取消订阅的方法。
非托管对象
非托管对象无论在什么时候,都不会被垃圾收集所回收,必须手动释放。
.net中的非托管资源都实现了IDispose接口,我们可以在使用的时候,使用using(){}类实现非托管资源的释放。
其中有一种情况非常容易遗漏,即通过一个方法创建了一个非托管的对象,如下所示:
public MemoryStream CreateAStream() { return new MemoryStream(); } |
var temp = CreateATemp();//CreateATemp返回一个非托管对象 |
bool InitializeStream(out MemoryStream stream) { stream = new MemoryStream(); return true; } |
集合/静态字段
对于集合,我们在使用完成之后,需要即时的clear,尤其是将一些方法中的临时变量添加到集合当中之后,会导致集合膨胀,并使得其中的内存泄露。
对于静态字段,我们应该尽量减少其可见的域,因为静态字段在整个程序运行期间都不会被释放,减少其可见域就减少了其内存泄露的可能性,注意,不到万不得以,千万不要声明静态的集合,就是使用了,那也一定要小心再小心。静态集合很容易造成内存泄露。
最好,大家有什么好的建议后者方法,欢迎补充!!
相关文章推荐
- 分享.net常见的内存泄露及解决方法
- 内存泄露及解决方法
- QuickReport报表Prepare之后造成内存泄露问题的解决方法
- Flex中Module的使用以及内存泄露问题解决方法
- 理解Java中的内存泄露及解决方法示例
- Flex内存泄露解决方法和内存释放优化原则
- 关于编译器 normal block输出(normal block at 0x0CECA1E8, ** bytes long)内存泄露解决方法
- 谈谈.NET中常见的内存泄露问题——GC、委托事件和弱引用
- Flex内存泄露解决方法和内存释放优化原则
- Android使用Handler造成内存泄露的分析及解决方法
- ATS插件开发中内存泄露问题的解决方法探讨
- 使用OpenCv库的MFC程序在退出后报内存泄露 可能解决方法
- AS3 unload() 与内存泄露解决方法
- C#之内存泄露以及解决方法
- 内存泄露的原因和解决方法
- Handler内存泄露原理及解决方法
- UIWebView的内存泄露问题解决方法
- 内存泄漏以及常见的解决方法
- 内存泄露之常见问题解决--初级篇