Oct 01

WinDbg调试小记:STAThread+COM调用+Console.ReadLine=内存泄漏

这两天发现某服务器进程出现内存泄漏,遂抓dump调试之。

调试内存泄漏首先当然是!dumpheap -stat:

.....

71861128    52018      2913008 System.Threading.Thread
71862cf0    22130      2962628 System.Int32[]
0040a230      198      3034188      Free
71e7fcf8   680643     13612860 System.Threading.SafeCompressedStackHandle
05ac45cc  1412552     22600832 System.Net.IPEndPoint
05fb5690   675403     24314508 UDT.UDTSocket
71860b54   727921     34970436 System.String
718542d4  1350915     37825620 System.UInt16[]
05a7b37c  1350904     54036160 System.Net.IPAddress

.....

一看吓了一跳,Thread应该是会被垃圾回收掉的,不可能会有这么多留在堆里面。于是看看Finalize Queue:

0:000> !finalizequeue
SyncBlocks to be cleaned up: 0
MTA Interfaces to be released: 0
STA Interfaces to be released: 0
----------------------------------
generation 0 has 16 finalizable objects (0f751b0c->0f751b4c)
generation 1 has 4 finalizable objects (0f751afc->0f751b0c)
generation 2 has 1719 finalizable objects (0f750020->0f751afc)
Ready for finalization 1406640 objects (0f751b4c->0fcaf60c)

.....

 

100W+个对象等待Finalize,基本可以确定Finalizer thread出问题了。切到里面看看Call stack:

0:000> ~2s
eax=00000001 ebx=ffffffff ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=77b191d5 esp=049eea00 ebp=049eea6c iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll!ZwWaitForSingleObject+0x15:
77b191d5 c20c00          ret     0Ch
0:002> k
ChildEBP RetAddr  
049ee9fc 77611270 ntdll!ZwWaitForSingleObject+0x15
049eea6c 776111d8 kernel32!WaitForSingleObjectEx+0xbe
049eea80 770bed85 kernel32!WaitForSingleObject+0x12
049eeaa4 771b9427 ole32!GetToSTA+0xad
049eead0 771b9b4d ole32!CRpcChannelBuffer::SwitchAptAndDispatchCall+0x132
049eebb0 770d37b4 ole32!CRpcChannelBuffer::SendReceive2+0xef
049eec28 770be868 ole32!CAptRpcChnl::SendReceive+0xaf
049eec7c 75dd1074 ole32!CCtxComChnl::SendReceive+0x95
049eec98 75dd102b rpcrt4!NdrProxySendReceive+0x49
049eeca4 75dd0146 rpcrt4!NdrpProxySendReceive+0xb
049ef0ac 75dd11ee rpcrt4!NdrClientCall2+0x18f
049ef0d0 75d45f22 rpcrt4!ObjectStublessClient+0x90
049ef0e0 770bf163 rpcrt4!ObjectStubless+0xf
049ef168 770a2b14 ole32!CObjectContext::InternalContextCallback+0x128
049ef1b8 72dc3fb7 ole32!CObjectContext::ContextCallback+0x87
049ef204 72dc4bcc mscorwks!CtxEntry::EnterContextOle32BugAware+0x2b
049ef324 72df9aeb mscorwks!CtxEntry::EnterContext+0x325
049ef358 72df9b90 mscorwks!RCWCleanupList::ReleaseRCWListInCorrectCtx+0xc4
049ef3a8 72d2befb mscorwks!RCWCleanupList::CleanupAllWrappers+0xdb
049ef3ec 72d2be0b mscorwks!SyncBlockCache::CleanupSyncBlocks+0xec

 

Google一番后发现,如果Finalizer thread的调用里面出现ole32!GetToSTA并且卡死的话,多半由于主线程为STA并且使用了COM,然后调用了阻塞的方法。(http://support.microsoft.com/?id=828988

这程序在初始化之后就调用了Console.ReadLine,工作全部在其它线程进行。程序并没有直接调用COM,但是使用了Remoting和MAF。推测是里面隐式调用了COM,导致出现了这问题。。。

解决方法那篇KB里面有给出,这里就不说了。话说要不是会一点WinDBG,应该就找不出问题根源了吧。。。M$的东西还真是。。。