UMDH を使用したユーザーモード メモリ リークの検出
ユーザー モード ダンプ ヒープ (UMDH) ユーティリティは、オペレーティング システムと連携して、特定のプロセスの Windows ヒープ割り当てを分析します。 UMDH は、特定のプロセスでメモリリークしているルーチンを特定します。
UMDH は、Windows 用デバッグ ツールに含まれています。 詳細については、「 UMDH」を参照してください。
UMDH を使用する準備
どのプロセスがメモリをリークするかをまだ特定していない場合は、まずそれを行います。 詳細については、「 パフォーマンス モニターを使用したUser-Modeメモリ リークの検出」を参照してください。
UMDH ログの最も重要なデータは、ヒープ割り当てのスタック トレースです。 プロセスがヒープ メモリをリークしているかどうかを判断するには、これらのスタック トレースを分析します。
UMDH を使用してスタック トレース データを表示する前に、 GFlags を使用してシステムを適切に構成する必要があります。 GFlags は、Windows 用デバッグ ツールに含まれています。
次の GFlags 設定では、UMDH スタック トレースが有効になります。
GFlags グラフィカル インターフェイスで、[イメージ ファイル] タブを選択し、プロセス名 (ファイル名拡張子を含む) を入力して Tab キーを押し、[ ユーザー モード スタック トレース データベースの作成] を選択し、[ 適用] を選択します。
または、同様に、次の GFlags コマンド ラインを使用します。 ここで、ImageName はプロセス名 (ファイル名拡張子を含む) です。
gflags /i ImageName +ust
完了したら、このコマンドを使用して GFlag 設定をクリアします。 詳細については、「 GFlags コマンド」を参照してください。
gflags /i ImageName -ust
既定では、Windows が収集するスタック トレース データの量は、x86 プロセッサでは 32 MB、x64 プロセッサでは 64 MB に制限されます。 このデータベースのサイズを大きくする必要がある場合は、GFlags グラフィカル インターフェイスで [イメージ ファイル] タブを選択し、プロセス名を入力して Tab キーを押し、[スタック バックトレース (Megs) チェック ボックスチェックし、関連付けられているテキスト ボックスに値 (MB 単位) を入力して、[適用] を選択します。 必要な場合にのみ、このデータベースを増やしてください。これは、Windows リソースが限られている可能性があるためです。 サイズを大きくする必要がなくなったら、この設定を元の値に戻します。
[ システム レジストリ ] タブでフラグを変更した場合は、これらの変更を有効にするために Windows を再起動する必要があります。 [ イメージ ファイル ] タブでフラグを変更した場合は、変更を有効にするためにプロセスを再起動する必要があります。 [ カーネル フラグ ] タブの変更はすぐに有効になりますが、次回 Windows が再起動すると失われます。
UMDH を使用する前に、アプリケーションの適切なシンボルにアクセスできる必要があります。 UMDH は、環境変数_NT_SYMBOL_PATHで指定されたシンボル パスを使用します。 この変数は、アプリケーションのシンボルを含むパスに設定します。 Windows シンボルへのパスも含める場合は、分析が完了している可能性があります。 このシンボル パスの構文は、デバッガーで使用される構文と同じです。詳細については、「 シンボル パス」を参照してください。
たとえば、アプリケーションのシンボルが C:\MySymbols にあり、C:\MyCache をダウンストリーム ストアとして使用して Windows シンボルにパブリック Microsoft シンボル ストアを使用する場合は、次のコマンドを使用してシンボル パスを設定します。
set _NT_SYMBOL_PATH=c:\mysymbols;srv*c:\mycache*https://msdl.microsoft.com/download/symbols
さらに、正確な結果を得るには、BSTR キャッシュを無効にする必要があります。 これを行うには、OANOCACHE 環境変数を 1 に設定します。 割り当てをトレースするアプリケーションを起動する前に、この設定を行います。
サービスによって行われた割り当てをトレースする必要がある場合は、OANOCACHE をシステム環境変数として設定し、この設定を有効にするために Windows を再起動する必要があります。
UMDH を使用したヒープ割り当ての増加の検出
これらの準備を行った後、UMDH を使用して、プロセスのヒープ割り当てに関する情報をキャプチャできます。 これを行うには、次の手順に従います。
調査する プロセスのプロセス ID (PID) を決定します。
UMDH を使用して、このプロセスのヒープ メモリ割り当てを分析し、ログ ファイルに保存します。 PID で -p スイッチを使用し、ログ ファイルの名前を指定して -f スイッチを使用します。 たとえば、PID が 124 で、ログ ファイルにLog1.txt名前を付ける場合は、次のコマンドを使用します。
umdh -p:124 -f:log1.txt
メモ帳または別のプログラムを使用してログ ファイルを開きます。 このファイルには、各ヒープ割り当ての呼び出し履歴、その呼び出し履歴を介して行われた割り当ての数、およびその呼び出し履歴で使用されたバイト数が含まれます。
メモリ リークを探しているため、1 つのログ ファイルの内容だけでは十分ではありません。 増加している割り当てを決定するには、異なる時間に記録されたログ ファイルを比較する必要があります。
UMDH では、2 つの異なるログ ファイルを比較し、それぞれの割り当てサイズの変更を表示できます。 大なり記号 (>) を使用して、結果を 3 番目のテキスト ファイルにリダイレクトできます。 バイト数と割り当て数を 16 進数から 10 進数に変換する -d オプションを含めることもできます。 たとえば、Log1.txtとLog2.txtを比較し、ファイル LogCompare.txtとの比較結果を保存するには、次のコマンドを使用します。
umdh log1.txt log2.txt > logcompare.txt
LogCompare.txt ファイルを開きます。 その内容は次のようになります。
+ 5320 ( f110 - 9df0) 3a allocs BackTrace00B53 Total increase == 5320
UMDH ログ ファイル内の呼び出し履歴 ("BackTrace" というラベルが付いている) ごとに、2 つのログ ファイル間で比較が行われます。 この例では、最初のログ ファイル (Log1.txt) は BackTrace00B53 に割り当てられた0x9DF0バイトを記録し、2 番目のログ ファイルには 0xF110 バイトが記録されています。つまり、2 つのログがキャプチャされた時間の間に0x5320追加のバイトが割り当てられました。 バイトは、BackTrace00B53 によって識別される呼び出し履歴から取得されました。
そのバックトレースの内容を確認するには、元のログ ファイル (たとえば、Log2.txt) のいずれかを開き、"BackTrace00B53" を検索します。結果は次のデータのようになります。
00005320 bytes in 0x14 allocations (@ 0x00000428) by: BackTrace00B53 ntdll!RtlDebugAllocateHeap+0x000000FD ntdll!RtlAllocateHeapSlowly+0x0000005A ntdll!RtlAllocateHeap+0x00000808 MyApp!_heap_alloc_base+0x00000069 MyApp!_heap_alloc_dbg+0x000001A2 MyApp!_nh_malloc_dbg+0x00000023 MyApp!_nh_malloc+0x00000016 MyApp!operator new+0x0000000E MyApp!DisplayMyGraphics+0x0000001E MyApp!main+0x0000002C MyApp!mainCRTStartup+0x000000FC KERNEL32!BaseProcessStart+0x0000003D
この UMDH 出力は、呼び出し履歴から割り当てられた0x5320 (10 進数 21280) の合計バイト数があることを示しています。 これらのバイトは、それぞれ0x428 (1064) バイトの0x14 (10 進数 20) の個別の割り当てから割り当てられました。
呼び出し履歴には "BackTrace00B53" という識別子が与えられ、このスタック内の呼び出しが表示されます。 呼び出し履歴を確認すると、 DisplayMyGraphics ルーチンが 新しい 演算子を介してメモリを割り当てていることがわかります。この演算子は、Visual C++ ランタイム ライブラリを使用してヒープからメモリを取得するルーチン malloc を呼び出します。
これらの呼び出しのうち、ソース コードに明示的に表示する最後の呼び出しを決定します。 この場合は、malloc の呼び出しが別の割り当てとしてではなく、new の実装の一部として発生したため、おそらく新しい演算子です。 したがって、DisplayMyGraphics ルーチンの新しい演算子のこのインスタンスは、解放されていないメモリを繰り返し割り当てしています。
フィードバック
https://aka.ms/ContentUserFeedback。
近日公開予定: 2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub イシューを段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、以下を参照してください:フィードバックの送信と表示