天天看点

OutOfMemoryException问题的处理方式搜集

MSDN上有关OutofMemoryException的介绍:

没有足够的内存继续执行程序时引发的异常。

命名空间:System

程序集:mscorlib(在 mscorlib.dll 中)

详见http://msdn.microsoft.com/zh-cn/library/system.outofmemoryexception(VS.80).aspx

下面这段有一些小技巧,转自:http://www.cnblogs.com/chainet/archive/2005/01/25/97000.html

介绍 

我所参与做的产品是一个比较大型的ASP.NET系统,在测试部门和客户那里,如果长时间运行,系统常常会出现一些OutOfMemoryException的异常。引起内存溢出的错误的原因有很多,主要在服务器配置方面和代码编写两个方面可以进行优化和改进,避免此类问题的出现,但完全杜绝是比较困难的。下面是我收集整理的一些解决方法。  

服务器配置方面

        1.   安装.NET Framework 1.1 Service Pack 1 

补丁部分解决了一些内存泄漏的问题,下载地址为:                a.打开/3GB Switch(如果你有3GB以上的内存)。这个配置只在Windows 2000 Advanced Server和Data Center版本以及Windows Server 2003以上才支持,参见:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenetchapt17.asp

http://support.microsoft.com/default.aspx?scid=kb;en-us;820108

                 b.即使你有很多内存,但.NET(注意不是ASP.NET工作进程,而是.NET整个使用的内存是有一定限制的,可以通过加大配置使用量来减少内存溢出的发生。方法如下:

修改machine.config文件,一般在 %System%\Microsoft.Net\Framework\v1.1.4322\CONFIG目录中, 修改processModel元素中的memoryLimit,大于缺省设置的60(意味着物理内存的60%)。

微软推荐的ASP.NET进程占用内存是不超过60%,并最好使计算出的实际值不超过800M。就是说,对于一台4G内存的服务器,最好将“memoryLimit”属性设置成“20”。设置一个适当的回收阈值,让IIS适时的进行进程回收,对于保证整个服务器的稳定运行,避免OutOfMemoryException是非常重要的。      

        3. 回收工作线程

设置IIS定期清除Work process是避免此异常的一个较好的方式。但这个功能是IIS 6.0(也就是Windows 2003上带的IIS)才支持。

配置方法如下:

修改IIS的应用程序池配置,选择DefaultAppPool(如果你的系统是用这个池),右键点属性->Recycling Setting,然后选择根据情况 修改“Recycle worker processes at the following times:'等几项配置,其中定时回收工作进程是一个比较好的方式,可以避免回收工作进程时,引起客户Session丢失。

Windows 2000 server 上安装的是IIS 5.0,本身不支持Recycle,但要想实现这个功能也不难。微软针对IIS提供的IIS5Recycle便是这样一个程序,它安装后以服务形式提供回收工作进程。

安装说明见http://support.microsoft.com/?id=322350

图片是表示安装好之后的配置信息! 是不是和IIS6中的一模一样?

OutOfMemoryException问题的处理方式搜集
OutOfMemoryException问题的处理方式搜集

代码编写方面的注意问题  

1.System.Drawing方面的类使用问题

System.Drawing用到了很多系统的资源和非托管代码,所以使用的时候要特别小心,注意内存泄漏(Memory Leak)例如:BitMap.MakeTransparent方法的使用问题:

http://www.dotnet247.com/247reference/msgs/40/202528.aspx

2.new byte[]问题

处理流的时候常常会用到new一个大的byte数组。但在多用户情况下会消耗大量的内存。正确的做法应该是定义一个比较小的byte数组做为缓存,然后循环使用。如在我们的程序中,有些地方使用不当,当图片(或附件)过大或过多的时候, new byte[length]就有可能消耗过多的内存。

3.  避免使用大对象数组或小对象大数组

编程时同样要重视效率问题(包括内存占用问题)。

4.Com接口调用是要注意释放对象。

我还使用过

GC.collect();语句,这个东东不是一定管用。但多少可以试试。http://stackoverflow.com/questions/2507489/outofmemoryexception-out-of-ideas这个地方有一堆老外的讨论,大意是显卡运行太多,有时候来不及运行GC,可以做个计时器定时GC回收,没太仔细看。刚开始使用Net的读者(甚至做了一两年商业开发的同行)可能对Net的内存泄露不是很了解,甚至会说Net不存在内存泄露,他们会问“不是有GC机制 吗?”恩,是有这么回事,它保证了通常应用时不用考虑头疼的资源释放问题,但很遗憾的是这个机制不保证你开发的程序就不存在内存泄露甚至可以说,Net内 存泄露是很常见的。这是因为: 一方面,GC机制本身的缺陷造成的;另一方面,Net中托管资源和非托管资源的处理是有差异的,托管资源的处理是由GC自动执行的,而非 托管资源 (占少部分,比如文件操作,网络连接等)必须显式地释放,否则就可能造成泄露。综合起来说的话,由于托管资源在Net中占大多数,通常不做显式的资源释放 是可以的,不会造成明显的资源泄露,而非托管资源则不然,是发生问题的主战场,是最需要注意的地方。

这个地方另一篇博客做了详细的介绍http://blog.163.com/chinese_zmm/blog/static/127440049201058115619235?fromdm&isFromSearchEngine=yes,提到了dispose的释放问题,摘要一些如下:

般来讲,我测性能时,主要关注Process里 以下几个指标,如果这些量整体来看是持续上升的,基本可以判断是有泄露情况存在的。

A.Handle Count

B.Thread Count

C.Private Bytes

D.Virtual Bytes

E.Working Set

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

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

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

在Run中输入perfmon.msc,运行,其他的自己摸索,不难。

3.2一些重要的性能计数器

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

4.如何制造出健壮的程序

4.1 Dispose()的使用

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

对于DB连接,COM组件(比如OLE组件)等必须调用其提供的Dispose方法,没有的话最好自己写一个。

4.2 using的使用

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

4.3 事件的卸载

这个不是必须的,推荐这样做。之前注册了的事件,关闭画面时应该手动注销,有利于GC回收资源。

4.4 API的调用

一般的使用API了就意味着使用了非托管资源,需要根据情况手动释放所占资源,特别是在处理大对象时。

4.5继承 IDisposable实现自己内存释放接口 4.6弱引用( WeakReference )

通常情况下,一个实例如果被其他实例引用了,那么他就不会被GC回收,而弱引用的意思是,如果一个实例没有被其他实例引用(真实引用),而仅仅是被弱引 用,那么他就会被GC回收。

使用了非托管资源的时候,可以自定义析构函数使得对象结束时释放所占资源;

对仅使用托管资源的对象,应尽可能使用它自身的Dispose方法,一般不推荐自定义析构函数。

这个有很牛逼的介绍http://msdn.microsoft.com/en-us/magazine/bb985010.aspx ,讲垃圾回收的,只可惜字太多了,时间来不及,先自己存着网址,以后看。

http://licheng3222.blog.163.com/blog/static/610993672011423105119257/?fromdm&isFromSearchEngine=yes对弱引用有个范例不错,Dispose也顺带说明了一下,并且讲了GC的发展历史,不错。

 4.弱引用(WeakReference)

  最后一个话题:弱引用。在编程中,对于那些大对象建议使用这种引用方式,这种引用不影响GC回收:我们用过了某个对象,然后将其至null,这样GC就可以快速回收它了,但是没过多久我们又需要这个对象了,没办法,只好重新创建实例,这样就浪费了创建实例所需的计算资源;而如果不至null,就会浪费内存资源。对于这种情况,我们可以创建一个这个大对象的弱引用,这样在内存不够时GC可以快速回收,而在没有被GC回收前我们还可以再次利用该对象。

  

Code

public class SomeObject

{

  。。。

}

public static void Main()

{

  SomeObject so = new SomeObject();

  WeakReference WRso = new WeakReference(so);

   so = null;

  Console.WriteLine(WRso.IsAlive); // True

  // 调用GC 手动回收。

  GC.Collect();

  Console.WriteLine(WRso.IsAlive); // False

}

  看到没,在so = null;后,它的弱引用依然是可用的。所以对于大对象的使用,aicken建议使用此种方式。另外,弱引用有长短之分:长弱引用在对象终结后,依然追踪对象;短弱引用则反之,aicken不建议人为干预GC的工作成果,所以推荐使用短弱引用,即上面代码中的方式。

这个文章http://archive.cnblogs.com/a/2042923/ 介绍了VS调试中修改内存的最大使用量方法。

另外 一个比较通用的办法,在Web应用程序的基类中通过try{}catch{}来主动捕捉OutOfMemoryException异常,发现该异常后直接调用GC.Collect()进行强制垃圾回收。

还有一个网站博客给了这样一个例子:多核的回收问题。

发现有这样的技术资料:

多處理器(或多核心)電腦上的 .NET CLR 的 Garbage Collection (GC) 機制預設是使用 Server Mode (伺服器模式) 在運作的,換句話說,就是「每一顆 CPU 都會有獨立的 GC 記憶體空間(堆積, Heap)」,所以如果你的 GC 記憶體空間 用掉了 500MB 且你有 4 顆 CPU 的話,就等於耗費了 2GB 的記憶體,進而發生 System.OutOfMemoryException 例外狀況!

要解決這個問題的方式就是將 GC 設定為 Workstation Mode (工作站模式),這樣就可以整台主機共用同一個 GC Heap,以節省記憶體的使用。要將預設的 GC 修改成 Workstation Mode 必須要修改 %WINDIR%\Microsoft.NET\Framework\v2. 0 . 50727 \Aspnet.config 檔案 ( 如果是 .NET 1 . 1 要修改 %WINDIR%\Microsoft.NET\Framework\v1. 1 . 4322 \Aspnet.config 檔案 ):

默认的 Aspnet.config 長這樣:

<?xml version= " 1.0 " encoding= " UTF-8 " ?>

<configuration>

<runtime>

<legacyUnhandledExceptionPolicy enabled= " false " />

<legacyImpersonationPolicy enabled= " true " />

<alwaysFlowImpersonationPolicy enabled= " false " />

<SymbolReadingPolicy enabled= " 1 " />

</runtime>

</configuration>

需加上一行 <gcServer enabled="false"/> 如下:

<?xml version= " 1.0 " encoding= " UTF-8 " ?>

<configuration>

<runtime>

<gcServer enabled= " false " />

<legacyUnhandledExceptionPolicy enabled= " false " />

<legacyImpersonationPolicy enabled= " true " />

<alwaysFlowImpersonationPolicy enabled= " false " />

<SymbolReadingPolicy enabled= " 1 " />

</runtime>

</configuration>

http://tieba.baidu.com/p/1109199097?pn=1这篇文章给了win7下修改最大使用内存的一个方法,很简单,值得试试,是>2G的32位win7 {{首先,开始---运行----CMD回车,在DOS提示符下输入BCDEDIT /SET PAE ForceEnable回车. 然后 在输入bcdedit /set IncreaseUserVa 3072回车.重启}}

继续阅读