浅析c#内存泄漏

一.概念

内存溢出:指程序在运行的过程中,程序对内存的需求超过了超过了计算机分配给程序的内存,从而造成“Out of memory”之类的错误,使程序不能正常运行。

造成内存溢出有几种情况: 1.计算机本身的内存小,当同时运行多个软件时,计算机得内存不够用从而造成内存溢出。对于这种情况,只能增加计算机内存来解决。 2.软件程序的问题,程序在运行时没能及时释放不用的内存,造成使用的内存越来越大从而造成内存溢出。对于这种情况,可以修改程序的代码来解决。

内存泄露:内存泄漏指由于疏忽或错误造成程序不能释放或不能及时释放已经不再使用的内存的情况,是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存不能回收和不能及时回收。当程序不能释放的内存越来越多是就会造成程序的性能下降或出现内存溢出的错误。

二、内存泄露检测工具:

1. SciTech Software AB .NET Memory Profiler-找到内存泄漏并优化内存使用针对C#,VB.Net,或其它.Net程序。

2. YourKit .NET & Java Profiler-业界领先的Java和.NET程序性能分析工具。

3. AutomatedQA AQTime-AutomatedQA的获奖产品performance profiling和memory debugging工具集的下一代替换产品,支持Microsoft, Borland, Intel, Compaq 和 GNU编译器。可以为.NET和Windows程序生成全面细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net 1.0,1.1,2.0,3.0和Windows 32/64位应用程序。

4. JavaScript Memory Leak Detector-微软全球产品开发欧洲团队(Global Product Development- Europe team, GPDE) 发布的一款调试工具,用来探测JavaScript代码中的内存泄漏,运行为IE系列的一个插件。

5.使用LoadRunner,使用方法http://www.cnblogs.com/mayingbao/archive/2007/12/20/1006818.html

6.使用 .Net Memory Profiler 工具,使用方法见:http://lzy.iteye.com/blog/344317

7.在单元测试时,在代码中检测,如.net 下   使用Console.WriteLine("Total memory: {0:###,###,###,##0} bytes", GC.GetTotalMemory(true));代码可以查看当前使用的内存。

二、导致内存泄露的常见情况及解决方法:

1.未退订的事件

是否没有手动注销事件就会造成内存泄露,我们先看这个问题

2.静态变量

静态变量中的成员所占的内存不果不手动处理是不会释放内存的,单态模式的对象也是静态的,所以需要特别注意。因为静态对象中的成员所占的内存不会释放,如果此成员是以个对象,同时此对象中的成员所占的内存也不会释放,以此类推,如果此对象很复杂,而且是静态的就很容易造成内存泄露。

3.非托管资源

因为非托管资源所占的内存不能自动回收,所以使用后必须手动回收,否则程序运行多次很容易造成内存泄露

4.Dispose方法没被调用,或Dispose方法没有处理对象的释放。这样也会造成内存泄露

5.当一个查询语句查询出来的数据量很大,达到几百万条数据时存放到datatable 或dataset中也会造成内存溢出,这是可以采用分页查询等其他方法来解决

 

 

以下是遇到过的内存优化-内存泄露的问题与应对方案。

场景:

1. Form.ShowDialog()问题。

private void button1_Click(object sender, EventArgs e)
{
new Form2.ShowDialog();
}
如果你觉得写这样的一段代码很Cool,很简洁,你在项目也有这么写代码,那你就碰到大麻烦了,你试试在Form2中开个大一点的数组来检查内存,然后运行,按几下button1按钮,你就会发现,内存一直增加,即使你调用GC也无济于事。


2. 窗体向全局性事件注册了事件的问题。

public Form2()
{
InitializeComponent();
MyApp.FormChanged += FormChanged;
}
MyApp是一个静态类,如果向这种类里面注册了事件,而又没有取消注册,这样也会遇到大麻烦。即使在外部已经记得调用了Form2实例的Dispose也是没用的。


实际上由于各个开发人员的水平跟接触面不同,又没有经过统一的培训(各个人对内存释放的理解与关注度不同,或者写代码时就没考虑内存未被释放这种问题),发现问题的时候项目往往已经做到了一个阶段(SIT测试),系统也比较庞大了,这种时候才发现内存泄露的问题确实是很头疼的。

 

解决方案:

1. 基于架构师的经验,或者统一意见会议,在开发前对开发人员进行必要的统一培训,对关系到性能、内存等会影响系统稳定性的解决方案进行培训。这本身既有助于开发人员水平的提高,也对系统的稳定性方面提供很大的保障(项目可不是做完一个就没了,这种培训会带来长久的增益效果)。

2. 注意托管资源与非托管资源的释放区别,非托管资源是需要手动释放的。

3. 使用using关键字,避免忘记Dispose的情况,如上面的ShowDialog问题。(using中还起到了try-catch的作用,避免由于异常未调用Dispose的情况)

4. 使用UnLoad事件或者析构函数,对注册的全局事件进行取消注册。

5. 特别注意自定义组件的稳定性更重要,发生问题时影响也更广。注意继承IDisposable接口,进行资源释放,并且对有疑问/复杂逻辑的地方添加try-catch语句。(发现问题的人员不一定有权限修改这些组件)一定要保证组件健壮健壮,再健壮。这是我做组件的感悟~~~多么痛的领悟!!

 

最后特别奉送一个"内存释放"的大招:

调用这个API能让你的内存一下爆减:贴一段网上的示例代码

[DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize")]
public static extern int SetProcessWorkingSetSize(IntPtr process, int minSize, int maxSize);
/// <summary>
/// 释放内存
/// </summary>
public static void ClearMemory()
{
GC.Collect();
GC.WaitForPendingFinalizers();
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}
是不是很给力,一调用内存就降下来。先别高兴太早,这其实是伪释放,只为暂时解决内存大量泄露导致系统崩溃而急需解决的情况。
具体原因:http://blog.sina.com.cn/s/blog_49f8960e0100081x.html,关键字:将物理内存转到虚拟内存,涉及磁盘读写。
 

 

浅析c#内存泄漏

上一篇:NX二次开发-NXOPEN C#UF创建圆柱theUfSession.Modl.CreateCyl1


下一篇:MongoDB入门学习(三):MongoDB的增删查改