有一个超出内存使用量(OutOfMemory)的异常,我该如何解决

 

有一个超出内存使用量(OutOfMemory)的异常,我该如何解决

 

问题:

有一个ASP.NET 的应用程序,经常性地会报内存不足的错误。OutOfMemoryExceptions

原因:

让我们来找找……

解决方法:

使用WinDbg来看看一个堆。

确实有泄漏么?

使用性能监视器来看看你的应用的内存事情情况,如果内存是在慢慢的增加,但从不减少,那么你有泄漏问题。如果它增长上去,降下来的时候像一个陡峭的峰,那你肯定是某个特定的操作使用了一大块的内存,然后被垃圾回收了。

如何才能确诊这些?

下面我会一步一步地带你来查找。

1)  得到一个dump文件。

这个使用WinDbgAdplus,如果你没有安装WinDbg,那先看看我前面的文章。

2)  打开dump文件,加载SOS

WinDbg中打开dump文件,加载SOS扩展

.load [path]\sos

3)  运行 dumpheap,运行下面的命令

   

!dumpheap -stat

它会显示在堆(heap)上的对象的统计信息,包含四列的摘要信息。

   对象的Method Table 信息。

   这类型对象的数量。

   这类对象的总大小(字节为单位)。

   对象类型的名字。

当心,不要忽视 –stat 这个参数,否则WinDbgdump出在整个堆上的每一个对象的地址,那你的屏幕上会有很多的信息。

4)  分析dumpheap出来的信息,下面是一部分输出:

0:000> !dumpheap -stat

Statistics:

MT             Count       TotalSize Class Name

7a7571a8           1              12

System.Net.DefaultCertPolicy

7a75661c           1              12

System.Diagnostics.TraceListenerCollection

........CONTINUED.........

68a884a0     641,952      38,517,120   System.Web.UI.LiteralControl

790fa3e0   1,431,594      122,806,484   System.String

Total 10,389,625 objects, Total size: 463,313,540

在这个dump中,有1,431,594个字符串,总大小是122MB879,515个对象,大小是43MB

在堆中的对象会比我们看到的要大

那个TotalSize列不是100%正确的。看LiteralControls 这个行,他们仅仅使用了38M,确实是这样么?这个TotalSize是指对象的结构而言的。但成员变量比如字符串 strings,整形 integers和其他子对象没有被包含进来,它仅仅是引起注意,否则,这个总的大小将是非常大的。LiteralControl 这个对象包含一组子对象,其中三个是字符串,它们的大小在System.String 对象上被列出来了。

使用 !dumpobj

让我们走进看看System.Web.UI.LiteralControls 这个对象,我们使用

!dumpheap -type System.Web.UI.LiteralControl,在屏幕被太多信息填充前,快速的按下CTRL+Break来中断。

0:000> !dumpheap -type System.Web.UI.LiteralControl

 Address       MT Size Gen

023ea0a8 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

你可以看到每个LiteralControl 对象是 60B的大小。和我前面说的一样,这只是代表对象的结构大小,并不包含引用的对象和属性。我们现在找一个地址,然后执行!dumpobject(简写:!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

68a273d0  4001fe6     18     System.Web.UI.Page    0 instance 01df4514 _page

……

790fa3e0  4002211     34          System.String    0 instance 02238664 _text

现在,找几个来看看,看看那些文本属性的值,那个最后面的在底部的,地址是02238664 的,拿来看看,很简单,使用 !do +adress

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:      </td>

    </tr>

  </table>

<!-- end of content table -->

 

Fields:

MT Field Offset Type VT Attr Value Name

790fed1c 4000096 4 System.Int32 0 instance 71 m_arrayLength

我们可以看到字符串包含了一些html代码中用来关闭table 表格的代码。

我们也可以检查这个对象中的其他属性,只要我们愿意这么做。但,另外有一个命令非常的有用。

使用 !objsize

有什么命令可以得到System.Web.UI.LiteralControl的大小么?非常简单,使用 !objsize ,这个命令查看所有的有这个对象的指针,然后计算它们的大小。运行 !help objsize 可以看到帮助信息。

在堆中的对象也可能比看起来的小

我们在LiteralControl 对象上运行 !objsize 看看,结果非常有趣,命令好像很忙碌的样子,花费很长的时间,我们得到了“事实上”的结果:

0:000> !objsize 023ea0a8

sizeof(023ea0a8) = 456918136 ( 0x1b3c0478) bytes (System.Web.UI.LiteralControl)

456MB,这怎么可能?我们回上去看看我们运行 !do LiteralControl 命令的地方,我们可以看到,这个控件有一个对page 的引用。这个page又对cache有引用,不久后,有了对整个堆的引用。这种重复的引用而导致的大小计算是不正确的。

总结:

希望这里能给你足够多的帮助,我们很快速的看了三个相关的简单命令,他们是:

  !dumpheap

!dumpobj

!objsize
原文地址:[url]http://www.sula.cn/90.shtml[/url]

posted @ 2008-01-28 14:38  softfair  阅读(1521)  评论(0编辑  收藏  举报