问题:
从Windows任务管理器里面观察到w3wp.exe进程的内存使用持续不断地往上长,最终整个进程崩溃,而这个w3wp.exe工作进程下面只有一个ASP.NET站点。
运行环境:
Windows 2003, IIS6.0, ASP.NET 2.0
详细分析:
针对此类问题,我们可以抓取hang dump来进行分析。微软给我们提供了一个很好的工具:http://support.microsoft.com/kb/286350
我们分别在w3wp.exe进程内存使用到600MB以及1GB的时候抓取了两个hang dump,使用到了如下的命令:
cscript.exe adplus.vbs -hang -p <PID>
这里的<PID>就是w3wp.exe进程的PID
接下来就需要使用windbg和SOS扩展来分析所抓取到的*.dmp文件。
Windbg可以从这里下载:http://www.microsoft.com/whdc/devtools/debugging/default.mspx
SOS存在于.NET framework:C:/Windows/Microsoft.NET/Framework/v2.0.50727/SOS.dll
关于SOS如何使用,可以参考:http://msdn.microsoft.com/zh-cn/library/bb190764.aspx
这一次很幸运的是,我们只要运行SOS的"!dumpdomain"命令就基本上能找到问题的原因了。从这条命令的输出结果里面,我们看到有无数的App_Web_XXXX.dll的Assembly
Assembly: 0e9feab0 [C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/root/f44aba29/1755568d/App_Web_g5xcboju.dll]
ClassLoader: 0fb80850
SecurityDescriptor: 02f7fd37
Module Name
0ee537d8 C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files/root/f44aba29/1755568d/App_Web_g5xcboju.dll
正常情况下,一个ASP.NET站点一般只有一个或者若干个App_Web_XXX.dll文件,而在我们的这个应用上面,这样的Assembly有上万个。另外,还记得我们前后分别在内存达到600MB和1GB的时候抓去了两个dump吗?很明显在第二个dump里面,Assembly的数量比第一个多了很多。
同时,我们还可以在windbg里面运行"!address -summary"命令,得到如下的结果
-------------------- Type SUMMARY --------------------------
TotSize ( KB) Pct(Tots) Usage
35450000 ( 872768) : 41.62% : <free>
1e90c000 ( 500784) : 23.88% : MEM_IMAGE
988000 ( 9760) : 00.47% : MEM_MAPPED
2b90c000 ( 713776) : 34.04% : MEM_PRIVATE
Largest free region: Base 4ebda000 - Size 06556000 (103768 KB)
以上是对第一个dump文件的"!address -summary"的结果,我们可以看到MEM_IMAGE占到了23.88%。这个MEM_IMAGE指的是当前进程里面所加载的dll占全部内存的大小,23.88%是一个不小的数目哦。更有甚者,在我们的第二个dump里面这个比例提高到了30%多。
从以上Assembly的数量在不断增多以及MEM_IMAGE的比例越来越大这个现象看来,.NET Framework在不断地编译出新的App_Web_XXX.dll并被加载了进来。
结论:
后来,与这个网站的开发者做了进一步交流之后,我们知道这个网站的aspx网页在网站运行过程中是在不断地增加的。于是,当有客户端访问新的aspx网页的时候,它需要被动态编译成Assembly,因此我们看到内存泄露的现象。要解决这个问题,我们必须避免动态地生成aspx页面,比如可以把这些页面静态化。