メタデータのトークン(ハンドル)。覚えていれば大体なんとかなります(とは。WinDbgでの解析のやっていきかたでは実際にWinDbgを利用してメモリの状態を調べてみましょう。メモリリークの原因を突き止める場合、一体何が多く残っているのか、何が理由で残っているのかといったことを探るのが定石です。その調査として出来そうな手順は以下のような感じでしょうか。Studioで見るのと大差なくめんどくさいだけといった感じなのですが、コマンドに慣れる意味でも駆使して見てまわってみましょう。Studioでの解析で利用したダンプを利用します。怪しそうな型を見つけるまずは「オブジェクトの統計情報を出力する」です。コマンドを使うと、ヒープにあるオブジェクトを型別で数とサイズを調べた結果を少ないほうから並べて出力してくれます。objects一覧を見て怪しそうな型を見つけます。あって怪しそうなのがわかります。いう型のオブジェクトの一覧を出力します。いうコマンドを利用することで指定した型の生きているオブジェクトの一覧を出力できます。結果のMT列の値を指定します。Aのオブジェクトのアドレス一覧を得ることができました。オブジェクトからGCのルートとなるオブジェクトを探す個々のオブジェクトを見つけることができたら次はそのオブジェクトがどこから参照されているのかを調べます。コマンドを利用します。このコマンドに先ほどの一覧のAddressを渡せばそのオブジェクトの参照元を調べて出力してくれます。handleとなっています。いう部分が見えていて、これはエントリポイントがなんやかんやで普段見えない何かなのであろうということが想像できます(ちょっと例が悪かったですね)。メソッドの途中のローカル変数などに保持されている場合には以下のような出力になります。ConsoleApp18.AWinDbgコマンドいろいろWinDbgというかSOS拡張のコマンドはたくさんあるのでメモリ周りでよく使いそうなものをいくつかまとめておきます。いうようなエラーが表示される初回はエラーが出るものの、再度実行すると何もなかったかのように動くようになります。-statヒープの使用状況、つまり型ごとのオブジェクトの数とその総サイズを取得します。-gc!HeapStatでは現在のヒープ領域とその中の世代別の使用量、空き容量を取得できます。コマンドでは各ヒープのサイズとアドレス、ジェネレーション別のアドレスを確認できます。キューの中身を確認できます。キューはFinalizerの呼び出しを待っているオブジェクトのキューです。それらのキューの状態を各ヒープ領域ごとに取得できます。Queueに移動してファイナライザーによって処理されます。各ヒープ領域ごとに情報が出力され、最後に統計が出力されます。キューにまわされます。addr>MethodTableのアドレスを元に現在どこからか参照があるであろう(GCに回収されない)アドレスの一覧を表示します。ConsoleApp15.Gのオブジェクト一覧を出力しています。objectsこの結果から1000オブジェクトが生きていることがわかります。GCで回収される)オブジェクトのアドレスの一覧を表示します。addr>指定したオブジェクトの参照元を出力します。対して実行すると以下のような結果が得られます。あることがわかります。対して実行するとルートとなるものがないことがわかります。roots).場合によってはこれからファイナライザーが実行するぞいっとなっているとFinalizerQueueから参照されていることがあります。addr>MethodTableのアドレスを指定して情報を出力します。オプションを付けて呼び出すとメソッド(MethodDescriptor)の一覧が出力されます。addr>EEClassのアドレスを指定して情報を出力します。queueが一向に減らない状態となり、回収されずにたまっていく一方となります。ファイナライザースレッドがブロックしているかどうかを調べるにはどうするかというお話です。コマンドでファイナライザースレッドを特定します。Uknこのリストでは2番目にFinalizerと書いてあるのでこれがファイナライザースレッドです。一覧できます(!ThreadsはCLRが知っている範囲の出力になります)。対象となるファイナライザースレッドが見つかったら先頭のIDを確認します。なっていることがわかります。スレッドを特定出来たら次にそのスレッドのスタックを見てみましょう。実行するとネイティブスタックを確認できます。ntdll!RtlUserThreadStart+0x21正常な場合にはこのようなスタック状態になるかと思います。WaitForFinalizerEventで次の呼び出しシグナルを待っているような感じでしょうか。似た実装であることが期待できるCoreCLRでもそんな感じなっている次にマネージコード上で無限ループに陥った場合のスタックです。いう文字列が見え、何やら処理中であることがわかります。実行するとCLR上のスタックを確認できます。実行中っぽいことがわかり、ファイナライザーがブロックされている可能性を疑えます(タイミングによっては単に実行中なだけかもしれませんが)。-thinlockなどがつかえそう)。AppendixWinDbgでもっと簡単に詳しくWinDbgとSOS拡張でいろいろ見れて便利!と思いきや、静的なフィールドの中身などをカジュアルに見ることはできません。そこでSOSEXというWinDbgの拡張を公開している方がいらっしゃるのでこれをありがたく使わせてもらいます。C:\Path\To\sosex.dllこのSOSEXを使うと以下のようなSOS拡張を強力にしたようなコマンドを使えるようになります。使えば指定した型の静的なフィールドの一覧を表示できます。ConsoleApp18]]]他にもあらかじめインデックスを作っておくことで走査を爆速にするとか強い機能があるので使いこなせるとよさそうです(最近知ったのでほとんど使ってなかったのです...)。ファイナライザーの実行を.NETアプリケーションコードから知りたいファイナライザーの実行タイミングを.NETアプリケーションコードから知りたいということもあるかもしれません。組み合わせることで検出可能です。場合に限るので素直にパフォーマンスモニターなどに頼るほうが良いかと思います。}});_thread.Start();まとめ.NETアプリケーションを実行していると時に解決がなかなか難しい問題にあたることがあり、そんなときメモリダンプが解決の糸口になる可能性があります。ダンプを眺めただけでは解決しない問題や、問題を発見したとしてもそもそもどうやって修正するのかというのはまた別の話です。解決の難しい問題は一つ一つ原因のようなものに見当をつけつつ追い詰めていく、その為にダンプを調査できるようになっておくのは悪くないと思いますのでお役に立てば幸いです。Studioでの可視化によるC#トラブルシューティングCTOの河合(@neuecc)です。»About株式会社グラニはC#を中心として、ASP.NET、Unity、VR開発を行っています。Inc.スターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。