1.用windbg或者VS加載SOS.dll, 可以參考我的另外一篇“如何用Visual Studio集成SOS.dll進行調試”
2.?
(1)用GCHandleLeaks調查
? ?具體示例如下:
0:003>?**!gchandleleaks**
-------------------------------------------------------------------------------
GCHandleLeaks will report any GCHandles that couldn't be found in memory.?????
Strong and Pinned GCHandles are reported at this time. You can safely abort the
memory scan with Control-C or Control-Break.??????????????????????????????????
-------------------------------------------------------------------------------
Found 249 handles:
0000000001dc1200?????0000000001dc1208?????0000000001dc1210????0000000001dc1218????
0000000001dc1220?????0000000001dc1228?????0000000001dc1230????0000000001dc1238????
0000000001dc1240?????0000000001dc1248?????0000000001dc1250????0000000001dc1258????
0000000001dc1260?????0000000001dc1268?????0000000001dc1270????0000000001dc1278????
0000000001dc1280?????0000000001dc1288?????0000000001dc1290????0000000001dc1298????
0000000001dc12a0?????0000000001dc12a8?????0000000001dc12b0????0000000001dc12b8????
…*(the list goes on)*
Searching memory
Found 0000000001dc1ff8 at location 000000000012ce48
Found 0000000001dc1380 at location 0000000000145870
Found 0000000001dc1390 at location 0000000000145918
Found 0000000001dc1378 at location 00000000001557f8
…
------------------------------------------------------------------------------
Some handles were not found. If the number of not-found handles grows over the
lifetime of your application, you may have a GCHandle leak. This will cause??
the GC Heap to grow larger as objects are being kept alive, referenced only??
by the orphaned handle. If the number doesn't grow over time, note that there
may be some noise in this output, as an unmanaged application may be storing?
the handle in a non-standard way, perhaps with some bits flipped. The memory?
scan wouldn't be able to find those.?????????????????????????????????????????
------------------------------------------------------------------------------
Didn't find 232 handles:
0000000001dc1200?????0000000001dc1208?????0000000001dc1210????0000000001dc1218????
0000000001dc1220?????0000000001dc1228?????0000000001dc1230????0000000001dc1238????
0000000001dc1240?????0000000001dc1248?????0000000001dc1250????0000000001dc1258????
0000000001dc1260?????0000000001dc1268?????0000000001dc1270????0000000001dc1278????
0000000001dc1280?????0000000001dc1288?????0000000001dc1290????0000000001dc1298????
...
"Didn't find XXX handles"就是我們需要關注的有可能泄露的句柄, 我們選一個進行查看, 如下:
0:003>?**!dumpobj poi(**0000000001dc1280)
Name: BloatedObject
MethodTable: 00000642801315c0
EEClass: 0000064280163cd0
Size: 32(0x20) bytes
(C:\pub\eetwc\windowh.exe)
Fields:
?????????????MT????Field???Offset?????????????????Type?VT????Attr????????????Value Name
000006423756d248??4000001????????8 System.IO.TextWriter??0 instance 0000000010353c28 tw
00000642375dba38??4000002???????10????????System.Byte[]??0 instance 0000000022e50080?**ba**
(2)利用dumpheap -stat和dumpobj
首先用dumpheap -stat列出所有類型的堆信息, 如下:
0:000> !dumpheap -stat
0x79c489a0??????????1????????????12 System.Runtime.Remoting.Messaging.ClientContextTerminatorSink
0x79bf9aec??????????1????????????12 System.IO.TextReader/NullTextReader
0x79be7078??????????1????????????12 System.Runtime.Remoting.Proxies.ProxyAttribute
0x79bce8e0??????????1????????????12 System.Runtime.InteropServices.ComVisibleAttribute
0x79bce7c8??????????1????????????12 System.CLSCompliantAttribute
0x79bc08e0??????????1????????????12 System.Empty
0x0618ae68??????????1????????????12 System.Web.Configuration.CustomErrorsConfigHandler
0x061887f8??????????1????????????12 System.Web.UI.WebControls.UnitConverter
0x06180848??????????1????????????12 System.Drawing.ColorConverter
0x05dbfbc4??????????1????????????12 System.Data.Res
第一列是地址,第二列是數量, 第三列是總共大小,第四列是具體類型名字,?需要注意的是第三列總共大小不包含成員變量的大小。
在其中找到異常的類型的堆地址, 比如有些類型的數量太大。 而后利用dumpobj來查看, 如下:
0:000>?!dumpobj?0x05dbfbc4
Name: System.Data.DataSet
MethodTable 0x060bbd2c
EEClass 0x060d2614
Size 80(0x50) bytes
GC Generation: 2
mdToken: 0x0200003b??(c:\windows\assembly\gac\system.data\1.0.5000.0__b77a5c561934e089\system.data.dll)
FieldDesc*: 0x060bb358
???????MT??????Field?????Offset?????????????????Type???????Attr??????Value Name
0x060b252c 0x4000583??????0x4????????????????CLASS???instance 0x00000000 site
0x060b252c 0x4000584??????0x8????????????????CLASS???instance 0x00000000 events
0x060b252c 0x4000582????????0????????????????CLASS?????shared???static EventDisposed
???>> Domain:Value 0x001192a0:NotInit??0x0017fc40:NotInit??0x044b7b28:0x1c357cb8 <<
0x060bbd2c 0x40003d3??????0xc????????????????CLASS???instance 0x00000000 defaultViewManager
0x060bbd2c 0x40003d4?????0x10????????????????CLASS???instance 0x3920ee28 tableCollection
0x060bbd2c 0x40003d5?????0x14????????????????CLASS???instance 0x3920ed9c relationCollection
0x060bbd2c 0x40003d6?????0x18????????????????CLASS???instance 0x00000000 extendedProperties
0x060bbd2c 0x40003d7?????0x1c????????????????CLASS???instance 0x1c357c90 dataSetName
0x060bbd2c 0x40003d8?????0x20????????????????CLASS???instance 0x182d0224 _datasetPrefix
0x060bbd2c 0x40003d9?????0x24????????????????CLASS???instance 0x182d0224 namespaceURI
0x060bbd2c 0x40003da?????0x40???????System.Boolean???instance 0 caseSensitive
0x060bbd2c 0x40003db?????0x28????????????????CLASS???instance 0x14309a0c culture
0x060bbd2c 0x40003dc?????0x41???????System.Boolean???instance 1 enforceConstraints
0x060bbd2c 0x40003dd?????0x42???????System.Boolean???instance 0 fInReadXml
0x060bbd2c 0x40003de?????0x43???????System.Boolean???instance 0 fInLoadDiffgram
0x060bbd2c 0x40003df?????0x44???????System.Boolean???instance 0 fTopLevelTable
0x060bbd2c 0x40003e0?????0x45???????System.Boolean???instance 0 fInitInProgress
0x060bbd2c 0x40003e1?????0x46???????System.Boolean???instance 1 fEnableCascading
0x060bbd2c 0x40003e2?????0x47???????System.Boolean???instance 0 fIsSchemaLoading
0x060bbd2c 0x40003e3?????0x2c????????????????CLASS???instance 0x00000000 rowDiffId
0x060bbd2c 0x40003e4?????0x48???????System.Boolean???instance 0 fBoundToDocument
0x060bbd2c 0x40003e5?????0x30????????????????CLASS???instance 0x00000000 onPropertyChangingDelegate
0x060bbd2c 0x40003e6?????0x34????????????????CLASS???instance 0x00000000 onMergeFailed
0x060bbd2c 0x40003e7?????0x38????????????????CLASS???instance 0x00000000 onDataRowCreated
0x060bbd2c 0x40003e8?????0x3c????????????????CLASS???instance 0x00000000 onClearFunctionCalled
0x060bbd2c 0x40003e9????????0????????????????CLASS?????shared???static zeroTables
???>> Domain:Value 0x0017fc40:NotInit??0x044b7b28:0x1c357c80 <<
如果想要查看對象的實際大小值,即也包含成員變量的大小。 請用!objsize?內存地址
我們也可以指定查看具體類型的堆地址, 請用!dumpheap -type?類型名
(3)使用clrstack和finalizeQueue
我們用clrstack查看當前gc中對象情況, 然后用finalizeQueue查看即將要被回收的對象情況, 兩者對比下,數量不對或者沒被回收的都有可能是內存泄露。
最后, 調查內存泄露是個費時費力還不一定有結果的事情, 祝愿大家都能找出具體問題。