fbird

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

对于.net的程序,大多数情况下的内存泄露都和对象绑定在事件上的没有被反注销有关,也有一部分是COM 没有解除引用,当然还有一些其它的状况。

 

当我们确定程序存在managed的内存泄露的时候,我们怎么去进一步确定泄露到底是哪些对象引起的呢?

第一步,我们让系统起起来,然后跑一些scenario来warm up,然后抓一个dump(有些情况下也可以在系统起起来之后马上抓dump)。对于64位机器可以直接在进程管理器里面选中进程右键create dump,对于32位的机器,可以用System Internals的Process Explorer(http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx)来抓取。

第二步,重复跑有内存泄露的scenario一定次数,比如说10,或者100次,根据你的需要定,然后再抓一个dump。

第三步,用windbg打开第一次抓的dump文件,在正确加载了symbol路径和SOS以后,运行命令”!dumpheap –stat”,这个命令将输出heap上的所有对象。将输出拷出来保存到文本文件,比如说1.txt。对第二次抓的dump做同样的操作,将输出文件保存到2.txt。

第四步,用比较工具比较1.txt和2.txt,看看哪些对象增长比较明显,特别是对象的数量是第二步中跑scenario的倍数。比如说第二步跑了100次,而堆上对象的数量就加了100或者说是几百个,那么很有可能这个对象存在泄露。至于比较工具我一般用WinMerge(http://winmerge.org/)

第五步,查看代码,看看疑似泄露的对象是在哪里创建的,是否确实没有被销毁,然后作出相应的修改。

大多数情况下,上面的五个步骤能解决掉问题。

 

但是有些情况下,比如说对象可能在很多地方被创建,而且挂在remoting事件上,这个时候就不太容易找到来源,对于这种情况我们一般在对象里面加一个字段用来记录这个对象创建时的call stack,然后在构造函数里面初始化这个字段。

    class Program

    {

        public string StackTrace;

 

        public Program()

        {

            StackTrace = Environment.StackTrace;

        }

 

    }

然后我们再重新测试,然后抓dump分析,下面是示例

1.根据前面”!dumpheap –stat”的比较结果,找到那些没有被释放的对象。可以根据对象的类型来找,也可以用前面的对象的method table来找。

0:115> !dumpheap -type My.CAddOnEventHelper (或者用 !dumpheap -mt <method table addr> in dumpheap -stat)

         Address               MT     Size

0000000010951af8 000007ff01b6d360      104    

0000000011593da0 000007ff01b6d360      104    

00000000117c3c10 000007ff01b6d360      104    

00000000120196f0 000007ff023edb60      104    

0000000012023720 000007ff023edb60      104    

0000000012989c48 000007ff023edb60      104    

0000000014d49de8 000007ff023edb60      104    

total 0 objects

Statistics:

              MT    Count    TotalSize Class Name

000007ff01b6d360        3          312 My.CAddOnEventHelper

000007ff023edb60        4          416 My.CAddOnEventHelper

2.在上面的对象中随便找一个,然后dump出来

0:115> !do 0000000010951af8

Name:        My.CAddOnEventHelper

MethodTable: 000007ff01b6d360

EEClass:     000007ff01cd3638

Size:        104(0x68) bytes

File:        C:\bin\My.Business.dll

Fields:

              MT    Field   Offset                 Type VT     Attr            Value Name

000007fee8d77248  4000d74        8 ...Channels.IChannel  0 instance 000000001524a778 channel

000007fee8d2d440  4000d76       30       System.Boolean  1 instance                1 connected

000007ff014b0e58  4000d79       10  System.Timers.Timer  0 instance 0000000010958930 timer

000007fee8d2d440  4000d7a       31       System.Boolean  1 instance                0 isDisposed

000007fee8d2d5a8  4000d7b       38 ...olean, mscorlib]]  1 instance 0000000010951b30 becIsDisabled

000007fee8d2d440  4000d7c       32       System.Boolean  1 instance                1 useGenuineChannels

000007fee8d2d440  4000d7d       33       System.Boolean  1 instance                0 inTimer

000007fee8d25880  4000d7e       18        System.Object  0 instance 000000001524ced8 remoteObject

000007fee8d26728  4000d7f       20        System.String  0 instance 0000000010351420 serverMachineName

000007ff01330ca8  4000d80       28 ...ConnectionManager  0 instance 0000000010951c68 connectionManager

000007fee8d25880  4000d75      a20        System.Object  0   shared           static connectionLock

                                 >> Domain:Value  0000000000129940:NotInit  0000000005f81fc0:00000000109407a8 0000000007477df0:0000000011a183c0 00000000074c9130:00000000111b8738 00000000074c9d90:0000000010958bb8 00000000075782e0:00000000113ab768 0000000007ba9640:0000000011b309d8 000000002afa00f0:NotInit  <<

000007fee8d2c610  4000d77      7d0         System.Int32  1   shared           static connectionCount

                                 >> Domain:Value  0000000000129940:NotInit  0000000005f81fc0:2 0000000007477df0:2 00000000074c9130:0 00000000074c9d90:4 00000000075782e0:5 0000000007ba9640:0 000000002afa00f0:NotInit  <<

000007fee8d49bb8  4000d78      a28 ...tions.IDictionary  0   shared           static remotingConfigEntries

                                 >> Domain:Value  0000000000129940:NotInit  0000000005f81fc0:0000000010940e10 0000000007477df0:0000000011a18b70 00000000074c9130:00000000111c29e8 00000000074c9d90:00000000109715f0 00000000075782e0:00000000113af458 0000000007ba9640:0000000011b31040 000000002afa00f0:NotInit  <<

000007fee8d2d440  40005d7       3c       System.Boolean  1 instance                0 initSynchronously

000007fee8d25880  40005d8       40        System.Object  0 instance 0000000010951c50 channelRegistrationLock

000007fee8d2d440  40005da       3d       System.Boolean  1 instance                0 isDisposed

000007fee8d2d440  40005dc       3e       System.Boolean  1 instance                0 staleData

000007fee8d26728  40005dd       48        System.String  0 instance 0000000010972130 StackTrace

000007ff01b6d4f8  40005de       50 ...angedEventHandler  0 instance 0000000000000000 optionChangedDelegate

000007ff01b6d4f8  40005df       58 ...angedEventHandler  0 instance 0000000000000000 LocalOptionChangedEvent

000007fee8d25880  40005d9      5f0        System.Object  0   shared           static gcRegistrationLock

                                 >> Domain:Value  0000000000129940:NotInit  0000000005f81fc0:NotInit  0000000007477df0:NotInit  00000000074c9130:NotInit  00000000074c9d90:0000000015262198 00000000075782e0:NotInit  0000000007ba9640:NotInit  000000002afa00f0:NotInit  <<

000007ff013303c8  40005db      5f8 ...ctionEventHandler  0   shared           static LocalRemoteConnectionEvent

                                 >> Domain:Value  0000000000129940:NotInit  0000000005f81fc0:NotInit  0000000007477df0:NotInit  00000000074c9130:NotInit  00000000074c9d90:0000000000000000 00000000075782e0:NotInit  0000000007ba9640:NotInit  000000002afa00f0:NotInit  <<

3.找到前面添加的StackTrace字段,然后dump出来

0:115> !dumpobj 0000000010972130

Name:        System.String

MethodTable: 000007fee8d26728

EEClass:     000007fee88aed68

Size:        4726(0x1276) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:         at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)

   at System.Environment.get_StackTrace()

   at My.CAddOnEventHelper..ctor() in D:\home\Business\Workflow\CCAddOnEventHelper.cs:line 110

   at My.CAddOnAgent..ctor() in D:\home\Business\Workflow\CCAddOnAgent.cs:line 64

   at My.CImageStatusMonitor.CCImageStatusMonitor.InternalInit(String uniqueId, ICCApplicationCoordinator masterController) in D:\home\Services\CCImageStatusMonitor\CCImageStatusMonitor.cs:line 609

   at My.CServiceBaseCore.Init(String uniqueId, ICCApplicationCoordinator masterController) in D:\home\Services\Common.Core\CCServiceBaseCore.cs:line 359

   at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)

   at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)

   at System.Runtime.Remoting.Messaging.ServerObjectTerminatorSink.SyncProcessMessage(IMessage reqMsg)

   at System.Runtime.Remoting.Messaging.ServerContextTerminatorSink.SyncProcessMessage(IMessage reqMsg)

   at System.Runtime.Remoting.Channels.CrossContextChannel.SyncProcessMessageCallback(Object[] args)

   at System.Runtime.Remoting.Channels.ChannelServices.DispatchMessage(IServerChannelSinkStack sinkStack, IMessage msg, IMessage& replyMsg)

   at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)

   at System.Runtime.Remoting.Channels.Ipc.IpcServerTransportSink.ServiceRequest(Object state)

   at System.Runtime.Remoting.Channels.SocketHandler.ProcessRequestNow()

   at System.Runtime.Remoting.Channels.SocketHandler.BeginReadMessageCallback(IAsyncResult ar)

   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

Fields:

              MT    Field   Offset                 Type VT     Attr            Value Name

000007fee8d2c610  4000103        8         System.Int32  1 instance             2350 m_stringLength

000007fee8d2b150  4000104        c          System.Char  1 instance               20 m_firstChar

000007fee8d26728  4000105       10        System.String  0   shared           static Empty

                                 >> Domain:Value  0000000000129940:0000000010351420 0000000005f81fc0:0000000010351420 0000000007477df0:0000000010351420 00000000074c9130:0000000010351420 00000000074c9d90:0000000010351420 00000000075782e0:0000000010351420 0000000007ba9640:0000000010351420 000000002afa00f0:0000000010351420 <<

这样,我们就知道了对象的来源,然后可以在对应的地方作修改。

这里给出了一种用windbg解决简单的托管对象内存泄露的方法。现在市场上有很多工具可以非常直观地找到泄露点,比如ANTS memory profiler(http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/ ).Net memory profiler(http://memprofiler.com/) ,可以供大家使用。

posted on 2014-03-21 14:22  PonyTan  阅读(531)  评论(0编辑  收藏  举报