「マイクロソフト系技術情報 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。
目次 †
概要 †
.NETのメモリ・リークについて、
ついて説明する。
GC †
マネージ・リソース †
- マネージ・リソースであれば、GCで適切に解放される。
- このため、マネージ・オブジェクトでリークするものは、
関数スタックに積まれるローカル変数以外に保持された
マネージ・オブジェクトである可能性が高い。
アンマネージ・リソース †
- アンマネージ・リソースを使用するマネージコードはリソースを明示的に解放する。
- これを怠ると、マネージコード上でGCが動作していてもメモリリークが発生する。
- マネージのユーザコードでアンマネージ・リソースをGCで適切に解放するには、以下の実装を施す必要がある。
Marshallクラス †
Marshallクラスを使用してアンマネージ・メモリ確保をする場合は
メモリの使用後に明示的な開放が必要になるので注意する。
(アンマネージ・メモリは.NETオブジェクトと異なりGC対象とならないため。)
IDisposable インターフェイス †
メンバにアンマネージ・リソースを持つ.NETオブジェクトには、
以下のIDisposableインターフェイスと対応する実装を施す必要がある。
- 参考
- IDisposable インターフェイス
http://msdn.microsoft.com/ja-jp/library/system.idisposable.aspx
IDisposableが適切に実装されていれば、例外時はGC任せで、
(未DisposeのオブジェクトはFinalizeでDisposeされる)
正常時にClose呼び出しをするだけで良い
(Close内でDisposeを呼び出すのが慣例の実装のため)。
- 例外的にCloseやファイナライザがあってもDisposeを明示的に呼び出す必要があるものもある
(例えば、旧ODP.NET、今は修正されている)が、これは、IDisposableの実装方針に反している。
- オブジェクトの世代によっては解放タイミングが遅れるためファイナライザの実行タイミングも遅れることになる。
これが問題となるような場合は、ファイナライザ任せで処理を実装しないこと(Disposeを明示的に呼び出す)。
LOH †
その他、LOH関係の参考資料 †
- The Dangers of the Large Object Heap
http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/
「ラージオブジェクトヒープの危険性」サンプル作成者のページ
- Large Object Heap fragmentation causes OutOfmemoryException? 報告者 Rudiger Klaehn
http://connect.microsoft.com/VisualStudio/feedback/details/521147/large-object-heap-fragmentation-causes-outofmemoryexception
上記(1)のサンプル作成者が米Microsoftに bug として報告したMicrosoftのフィードバック用ページ
- .NET 航海日誌: LOH (ラージオブジェクトヒープ) でフラグメンテーションが起こる !?
http://dotnetlogbook.blogspot.jp/2009/10/loh.html
上記(1)を、日本語で要約したページ
- torutkの日記: .NETのメモリ管理と断片化問題
http://d.hatena.ne.jp/torutk/20100529/p1
SOS.dll †
WinDbg+SOS拡張によるマネージ・ライブ・デバック例 †
久しぶりに使ったので手順などをメモ(幾つか補足も記載)。
Book1.xlsx
開始 †
- VSのイミディエイト・ウィンドウからもSOSデバッガ拡張コマンドを実行できる。
- WinDbgから利用することもできるが、VSの環境変数を使う場合は、CMDから
vsvars32.batを実行後にWinDbg.exeを起動して対象プロセスにアタッチする。
※ 同様の操作をマネージ・ダンプに対して行う事もできる。
以下はSOSデバッガ拡張コマンドの実行例。
- !DumpHeap?コマンドでは、
- ヒープのオブジェクト情報を一覧できる。
- このコマンドは、メモリリークなどの問題を確認するのに有効である。
- 本番環境で動作するメモリリークをデバッグする場合は、WinDbgなどのデバッガを使用すると良い(WindowsSDK?)。
- また、-Typeオプションを指定することで、任意の型のオブジェクトのみ一覧できる。
(1)オブジェクト情報の確認方法 †
!DumpObj 013ac060
Name: WindowsFormsApplication1.Class1
MethodTable: 009e71fc
EEClass: 00f01fb8
Size: 20(0x14) bytes
Fields:
MT Field Offset Type VT Attr Value Name
79332d70 4000006 4 System.Int32 1 instance 9 a
79332d70 4000007 8 System.Int32 1 instance 9 b
79332d70 4000008 c System.Int32 1 instance 9 c
79332d70 4000009 24 System.Int32 1 static 9 x
79332d70 400000a 28 System.Int32 1 static 9 y
79332d70 400000b 2c System.Int32 1 static 9 z
DumpObj?に指定したオブジェクトのアドレスから
メモリ情報([メモリ] ウィンドウ)を確認すると
メンバ変数の情報を確認することができる。
!DumpClass 00f01fb8
Class Name: WindowsFormsApplication1.Class1
Parent Class: 790c3ef0
Module: 009e2c5c
Method Table: 009e71fc
Vtable Slots: 4
Total Method Slots: 6
Class Attributes: 100001
NumInstanceFields: 3
NumStaticFields: 3
MT Field Offset Type VT Attr Value Name
79332d70 4000006 4 System.Int32 1 instance a
79332d70 4000007 8 System.Int32 1 instance b
79332d70 4000008 c System.Int32 1 instance c
79332d70 4000009 24 System.Int32 1 static 9 x
79332d70 400000a 28 System.Int32 1 static 9 y
79332d70 400000b 2c System.Int32 1 static 9 z
こちらでは、インスタンス変数の値は表示されない。
また、上記のフィールド情報はilasmを使用して確認できるらしいが、情報を確認できず。
!DumpModule 009e2c5c
Attributes: PEFile
Assembly: 001b2478
LoaderHeap: 00000000
TypeDefToMethodTableMap: 009e00c0
TypeRefToMethodTableMap: 009e00dc
MethodDefToDescMap: 009e0194
FieldDefToDescMap: 009e01d4
MemberRefToDescMap: 009e0204
FileReferencesMap: 009e02dc
AssemblyReferencesMap: 009e02e0
MetaData start address: 00402440 (4084 bytes)
!DumpMT -md 009e71fc
EEClass: 00f01fb8
Module: 009e2c5c
Name: WindowsFormsApplication1.Class1
mdToken: 02000006 BaseSize: 0x14
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
79286aa0 79104944 PreJIT System.Object.ToString()
79286ac0 7910494c PreJIT System.Object.Equals(System.Object)
79286b30 7910497c PreJIT System.Object.GetHashCode()
792f7510 791049a0 PreJIT System.Object.Finalize()
009ec160 009e71e4 JIT WindowsFormsApplication1.Class1..ctor()
009ec168 009e71f0 JIT WindowsFormsApplication1.Class1..cctor()
(2)メモリリークの確認方法 †
* e !clrstackで、全てのマネージスレッドとスタックトレースし、ファイナライザスレッドのハングを確認する。
~* e !clrstack
OS Thread Id: 0x1a30 (0)
ESP EIP
0012f32c 7c94e514 [InlinedCallFrame: 0012f32c] System.Windows.Forms.UnsafeNativeMethods.WaitMessage()
0012f328 7b1d8ed8 System.Windows.Forms.Application+ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32, Int32, Int32)
0012f3c4 7b1d89c7 System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext)
0012f418 7b1d8811 System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext)
0012f448 7b195921 System.Windows.Forms.Application.Run(System.Windows.Forms.Form)
0012f45c 00f300ae WindowsFormsApplication1.Program.Main()
0012f688 79e71b4c [GCFrame: 0012f688]
OS Thread Id: 0x1a44 (1)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
OS Thread Id: 0x16c8 (2)
Failed to start stack walk: 80004005
OS Thread Id: 0xa14 (3)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
OS Thread Id: 0x11fc (4)
Unable to walk the managed stack. The current thread is likely not a
managed thread. You can run !threads to get a list of managed threads in
the process
参考資料 †
Silverlightの場合 †
開始 †
WinDbgとSilverlightをインストールしておく。
オブジェクト情報の確認方法 †
以下WinDbgにてSilverlightのダンプファイルから、オブジェクト数を確認する手順の例。
アタッチ先を「iexplore.exe」などに設定すれば、ライブ・デバックも可能であるもよう。
- メニュー[File]-[Open Crush Dump]からダンプファイルを開く
- 以下コマンドで全てのオブジェクト数を取得する
!dumpheap -stat
- 他のダンプファイルを解析する際は、再度WinDbgを実行する。
Silverlight Toolkitの問題 †
Silverlight Toolkitにメモリリークの問題があります。
We encountered this bug too, but took a slightly different tact -- we used a weak event listener pattern.
- We used:
if (null != _rootVisual)
{
var rootVisual = _rootVisual;
// Use a weak event listener.
var rootVisualMouseMoveListener = new WeakEventListener<ContextMenu, object, MouseEventArgs>( this );
rootVisualMouseMoveListener.OnEventAction = ( instance, source, eventArgs ) => instance.HandleRootVisualMouseMove( source, eventArgs );
rootVisualMouseMoveListener.OnDetachAction = ( weakEventListener ) => rootVisual.MouseMove -= weakEventListener.OnEvent;
rootVisual.MouseMove += rootVisualMouseMoveListener.OnEvent;
}
ここの「we used:」が修正コード。
参考資料 †
その他 †
Visual Studio Enterprise Edition †
Visual Studio2015 Enterprise Edition ではマネージ メモリの分析機能が
強化されており、!DumpHeap? コマンドで実現できることに近い内容が確認できる。
その他の拡張 †
.NET Framework アプリケーションのデバッグに有用
(ただし、サポートは提供されておらずドキュメントや公開情報も少ない状況)
psscor4 †
.load "C:\\Psscor4\\x86\\x86\\psscor4.dll"
!psscor4.help
mex †
.load mex.dll
!mex.help
Tags: :障害対応, :性能, :デバッグ