客户服务器上的系统已经稳定运行好几年了,在客户更换过新服务器(双4核CPU,8G内存)一段时间后近期突然无法正常使用,打开首页或登录成功后,立即就出现 System.OutOfMemoryException 的错误,系统彻底崩了。
询问过谷哥和度娘后,得到的结论是:
在ASP.NET Web(U8Soft.AppServer)服务器上,ASP.NET所能够用到的内存,通常不会等同于所有的内存数量。在machine.config配置文件(目录)中,配置节中有一个属性“memoryLimit”,这个属性的值是一个百分值,默认为“ 60 ”,即指定了ASP.NET进程(在任务管理器中大家就可以看到ASP.NET的进程,IIS5中为aspnet_wp,IIS6中为w3wp)能够使用所有物理内存的60%。当ASP.NET使用的内存量超过这个限额时,IIS会开始自动回收(recycle)进程,即创建一个新的进程去负责应付Http请求,而将旧进程所占用的内存回收。 ??当我们有一台很大内存的服务器时,“memoryLimit”这个值是需要进行适当的调整的。比如我们准备了一台4G内存的服务器,那么4G× 60 %= 2 .4G。但是,对于Win32操作系统,一个进程所能占用的所有内存空间只有2G。当ASP.NET进程占用的内存开始达到2G时,由于它并没有达到2.4G的“回收阈值”,所以IIS不会启动recycle进程操作,但是由于Win32的限制,实际上已经不能给这个进程分配更多的内存了,于是,OutOfMemoryException就很可能会被抛出了。为了避免这样的情况,我们就必须将“memoryLimit”适当调小,以让IIS更早的进行进程回收。 ??微软推荐的ASP.NET进程占用内存是不超过60%,并最好使计算出的实际值不超过800M。就是说,对于一台4G内存的服务器,最好将“memoryLimit”属性设置成“ 20 ”。设置一个适当的回收阈值,让IIS适时的进行进程回收,对于保证整个服务器的稳定运行,避免OutOfMemoryException是非常重要的。 ??在IIS6中,ASP.NET进程的回收阈值不再由配置节中的“memoryLimit”属性决定,而是由IIS管理器中的应用程序池配置中的设置决定。 ??但是,即使正确设置了这些配置,也不能保证完全避免OutOfMemoryException的发生,原因可能是多样而复杂的,比如内存回收操作可能耗时太多等等。开发人员要注意的,就是在代码中时刻牢记不要无谓的使用和浪费内存。
按此方法,几经修改配置,仍无任何效果,于是进一步怀疑是各杀毒软捣的鬼,特别是麦咖啡的"缓冲区溢出保护",一看就是做内存访问限制的,于是第一个先将它停止,结果发现在将麦咖啡的"缓冲区溢出保护"停止后,系统恢复正常。但过几十秒钟后,又复崩溃。Why? 再检查麦咖啡,发现"缓冲区溢出保护"自动启动了......采用了N种方法将其停用无果,求助麦咖啡的客服无用,只能将麦咖啡从服务器上彻底删除,于是一切又恢复了正常。
最近进一步研究此问题,发现有这样的技术资料:
多處理器(或多核心)電腦上的 .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>
不过像这种出現 Out of Memory 的异常状况在 64 位位的操作系统几乎不会出现问题,所以如果你是用 64 位的操作系统(如:Windows Server 2003 64bit ) 的話,是不太需要做这样的调整的,但你的内存还是要够多才行,否则过多的分页況还是会降低你 ASP.NET 就用程序的执行效率!
除了这点之外,在正式(Production)环境中的 ASP.NET 应用程序一定要在 web.config 中关闭开发时期设定的 debug 与 trace,才不会内存造成额外的负担。因为 ASP.NET应用程序在启用debug mode 的时候所有的 Symbol 档(*.pdb)都会被载入内存中,不但会消耗不少内存,也会降低执行效率。