发现并解决ASP.NET内存耗尽(OOM),让服务器"永不重启"
来源:http://www.cnblogs.com/koumi/archive/2010/10/12/1849077.html
========下面的一堆文字为了说明一件事情---.NET程序,内存溢出,如何控制.主要是堆HEAP大小如何控制以及优化.以减轻GC突发性负担及这个时候服务器当机的可能*.
对于大型程序,完全依赖GC是不现实的,对于高负载服务器,往往我们80%的堆都由自己的析构函数接管,并辅助以自行设计的bufferpool接管堆释放工作以达到HEAP可控的目的,减少CPU突发性负荷(CPU尖峰).虽然不像C那样可以控制的那么完全,但是多多少少对OOM的发生起到抑制作用,深入下去可以完全避免OOM......好了IF性能和内存开销没什么追求的 THEN 就不必看了,,,,
ELSE
GO
=====================
1.下个windbg,去baidu google一下即可..
2.sos.dll 这个框架自带,只所以提下,是让大家有搜索的关键字用.
3.泄露,对于.net程序,主要是堆泄露,对于大型服务器程序,需要严格排查OOM(内存泄露)的问题,节约服务器GC开销,内存开销,cpu开销,提高支撑
4.配置环境变量 添加系统变量 _NT_DEBUGGER_EXTENSION_PATH C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 这个是为了能够找到sos.dll
5.实战
启动windbg,开启调试窗口,加载用于.NET调试所使用的sos.dll
.load sos
回车之后无任何显示表示无任何错误,这个时候就可以运行调试命令了.
需要说明的是.打头的是windbg自带命令,!打头的命令是sos.dll调试命令.
显示GC堆占用情况命令 !dumpheap -stat
大概会显示这些:对象表,对象数量,每个对象内存占用情况...等,如下:
---------------------
0:000> !dumpheap -stat
Statistics:
MT Count TotalSize Class Name
7a787cc4 1 12 System.IO.FileSystemWatcher+FSWAsyncResult
7a75904c 1 12 System.Diagnostics.OrdinalCaseInsensitiveComparer
7a7575cc 1 12 System.CodeDom.Compiler.CodeDomConfigurationHandler
7a7571a8 1 12 System.Net.DefaultCertPolicy
7a75661c 1 12 System.Diagnostics.TraceListenerCollection
7a755834 1 12 System.Diagnostics.PerformanceCounterCategoryType
..................
68a66a88 227,559 12,743,304 System.Web.UI.WebControls.Literal
68a2f7fc 399,272 14,373,792 System.Web.UI.ControlCollection
68a92e2c 768,731 33,824,164 System.Web.UI.Control+OccasionalFields
68a884a0 641,952 38,517,120 System.Web.UI.LiteralControl
79124228 879,515 43,394,976 System.Object[]
790fa3e0 1,431,594 122,806,484 System.String
Total 10,389,625 objects, Total size: 463,313,540
----------------------
1,431,594 个 System.String对象 占用 122 MBytes
使用!dumpobj 可以查看System.String对象构成情况
0:000> !dumpheap -type System.Web.UI.LiteralControl
Address MT Size Gen
023ea0a8 68a884a0 60 2 System.Web.UI.LiteralControl
023ea0e4 68a884a0 60 2 System.Web.UI.LiteralControl
023ea374 68a884a0 60 2 System.Web.UI.LiteralControl
023ea460 68a884a0 60 2 System.Web.UI.LiteralControl
023ea510 68a884a0 60 2 System.Web.UI.LiteralControl
023eab3c 68a884a0 60 2 System.Web.UI.LiteralControl
........CONTINUED........
023fe31c 68a884a0 60 2 System.Web.UI.LiteralControl
023fe414 68a884a0 60 2 System.Web.UI.LiteralControl
023fe4c4 68a884a0 60 2 System.Web.UI.LiteralControl
023fe500 68a884a0 60 2 System.Web.UI.LiteralControl
哦 基本上都是LiteralControl
随便抓个控件的地址来分析!do命令
0:000> !do 023ea0a8
Name: System.Web.UI.LiteralControl
MethodTable: 68a884a0
EEClass: 68a88428
Size: 60(0x3c) bytes
GC Generation: 2
(C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
MT Field Offset Type VT Attr Value Name
790fa3e0 4001fe0 4 System.String 0 instance 00000000 _id
790fa3e0 4001fe1 8 System.String 0 instance 00000000 _cachedUniqueID
68a2af44 4001fe2 c ...em.Web.UI.Control 0 instance 023e8864 _parent
68a91070 4001fe3 2c System.Int32 0 instance 0 _controlState
68a85ea0 4001fe4 10 ...m.Web.UI.StateBag 0 instance 00000000 _viewState
68a2af44 4001fe5 14 ...em.Web.UI.Control 0 instance 023e8864 _namingContainer
68a273d0 4001fe6 18 System.Web.UI.Page 0 instance 01df4514 _page
68a92e2c 4001fe7 1c ...+OccasionalFields 0 instance 00000000 _occasionalFields
68a2b378 4001fe8 20 ...I.TemplateControl 0 instance 00000000 _templateControl
68a14528 4001fe9 24 ...m.Web.VirtualPath 0 instance 00000000 _templateSourceVirtualDirectory
68a8bb48 4001fea 28 ...rs.ControlAdapter 0 instance 00000000 _adapter
68a3a8f8 4001feb 30 ...SimpleBitVector32 1 instance 023ea0d8 flags
790f9c18 4001fda c70 System.Object 0 shared static EventDataBinding
>> Domain:Value 000f0d00:NotInit 0011a720:01df0028 <<
790f9c18 4001fdb c74 System.Object 0 shared static EventInit
>> Domain:Value 000f0d00:NotInit 0011a720:01df0034 <<
790f9c18 4001fdc c78 System.Object 0 shared static EventLoad
>> Domain:Value 000f0d00:NotInit 0011a720:01df0040 <<
790f9c18 4001fdd c7c System.Object 0 shared static EventUnload
>> Domain:Value 000f0d00:NotInit 0011a720:01df004c <<
790f9c18 4001fde c80 System.Object 0 shared static EventPreRender
>> Domain:Value 000f0d00:NotInit 0011a720:01df0058 <<
790f9c18 4001fdf c84 System.Object 0 shared static EventDisposed
>> Domain:Value 000f0d00:NotInit 0011a720:01df0064 <<
79124228 4001fec c88 System.Object[] 0 shared static automaticIDs
>> Domain:Value 000f0d00:NotInit 0011a720:01df0070 <<
790fa3e0 4002211 34 System.String 0 instance 02238664 _text
这里如果发现某个值过大,就可以继续深入使用!do + 地址 来进入查看,以便找到OOM的根源..试试
0:000> !do 02238664
Name: System.String
MethodTable: 790fa3e0
EEClass: 790fa340
Size: 158(0x9e) bytes
GC Generation: 2
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
Fields:
MT Field Offset Type VT Attr Value Name
790fed1c 4000096 4 System.Int32 0 instance 71 m_arrayLength
790fed1c 4000097 8 System.Int32 0 instance 70 m_stringLength
790fbefc 4000098 c System.Char 0 instance 3c m_firstChar
790fa3e0 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000f0d00:790d6584 0011a720:790d6584 <<
79124670 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000f0d00:01d413b8 0011a720:01d44f80 <<
可以看到32位地址为02238664的是用于表格的结束标志存储.
使用!objsize进一步命令查看对象内的的对象构成,精确到class名,自己可以试试了...还有很多命令可以查看Gen的回收情况,对于长时间未释放的unmanaged资源也可以列表出来,这个时候可以do进去,查看内部结构,找到属于哪段代码之后,再返回自己的.cs代码进行逻辑调整....这样的话,性能就很可观了.同时可以尽量使用好CPU,而不必让不必要的CPU时间牺牲框架和底层的代码胶合层上面..永远要记住CPU时间是用户的,不是你的!这样,对内存了解的多一些,你的程序就能跑的更快一点,持久运行的时间就更长一些...
通常,asp.net或者socket服务器运行的时候,对内存的要求是比较高的,如果内存的性能我们不能去把握,真的很难再生产环境中超越LAMP体系架构.
我的观点是,内存---能节约,尽量节约吧,能高效利用,尽量高效利用!所以写这篇帖子,非常非常肤浅的介绍了下,前后也就写了20分钟,因为要睡觉了...
----[成都]CCCP苏联程序员
对了可以去买本"windows调试技术"来看 http://bbs.dbgtech.net/forumdisplay.php?fid=16 可以先了解下.