Visual Studio でのメモリ使用量の測定 (C#、Visual Basic、C++、F#)

デバッガーに統合されたメモリ使用量診断ツールを使用したデバッグ中に、メモリ リークおよび非効率的なメモリを見つけます。 メモリ使用量ツールを使うと、マネージド メモリ ヒープとネイティブ メモリ ヒープの 1 つまたは複数の "スナップショット" を取得して、オブジェクト型のメモリ使用量への影響を理解するのに役立てることができます。 デバッガーがアタッチされていない状態で、または実行中のアプリをターゲットにして、メモリ使用率を分析することもできます。 詳細については、「デバッガーを使用して、または使用せずにプロファイリング ツールを実行する」を参照してください。 ニーズに最適なメモリ分析ツールを選択する方法については、「メモリ分析ツールの選択」を参照してください。

メモリ使用量 ツールでメモリのスナップショットをいつでも収集できますが、Visual Studio デバッガーを使用すると、パフォーマンスの問題を調査中にアプリケーションの実行方法を制御することができます。 ブレークポイントの設定、ステップ実行、すべて中断、その他のデバッガー アクションは、パフォーマンスの調査を最も関連性の高いコード パスに集中させるのに役立ちます。 アプリの実行中にこれらのアクションを実行することで、目的としていないコードからのノイズを除去することができ、問題の診断にかかる時間を大幅に短縮できます。

重要

デバッガーが統合された診断ツールは、Visual Studio の .NET 開発 (ASP.NET や ASP.NET Core など)、ネイティブ/C++ 開発、混合モード (.NET とネイティブ) アプリで利用できます。 Windows 8 以降では、デバッガーを使用してプロファイル ツールを実行する必要があります ( [診断ツール] ウィンドウ)。

このチュートリアルでは、次の作業を行います。

  • メモリのスナップショットの作成
  • メモリ使用量データの分析

メモリ使用率では必要なデータを得ることができない場合、パフォーマンス プロファイラーの他のプロファイリング ツールが別の種類の情報を提供します。その情報が役に立つ可能性があります。 多くの場合、アプリケーションのパフォーマンス上の問題は、CPU、UI のレンダリング、ネットワークの要求時間など、メモリ以外の何かが原因になります。

Note

カスタム アロケーター サポート ネイティブ メモリ プロファイラーは、実行時に生成された割り当て ETW イベント データを収集して機能します。 CRT および Windows SDK のアロケーターには、割り当てデータをキャプチャできるように、ソース レベルで注釈が付けられています。 独自のアロケーターを作成する場合、新しく割り当てられたヒープ メモリへのポインターを返すすべての関数は、__declspec(アロケーター) で修飾できます。myMalloc での例を次に示します。

__declspec(allocator) void* myMalloc(size_t size)

メモリ使用量データの収集

  1. Visual Studio でデバッグするプロジェクトを開き、メモリ使用率を調べ始めるポイントでアプリのブレークポイントを設定します。

    メモリの問題があると疑われる領域がある場合は、メモリの問題が発生する前に、最初のブレークポイントを設定します。

    ヒント

    アプリがメモリの割り当てと割り当て解除を頻繁に行う場合、目的とする操作のメモリ プロファイルを取得するのが容易ではないため、操作の最初と最後にブレークポイントを設定して (または操作をステップ実行して)、メモリが変更された正確なポイントを見つけます。

  2. 分析するコードの関数またはリージョンの終わりに (または疑わしいメモリの問題が発生したあとに) 2 つ目のブレークポイントを設定します。

  3. [診断ツール] ウィンドウは、オフにしていない限り自動的に表示されます。 もう一度ウィンドウを表示するには、 [デバッグ]>[ウィンドウ]>[診断ツールの表示] の順にクリックします。

  4. ツールバーにある [ツールの選択] の設定で、 [メモリ使用量] を選択します。

    Screenshot of Diagnostics Tools.

    Screenshot of Diagnostics Tools.

  5. [デバッグ]、[デバッグの開始] の順にクリックします (あるいは、ツール バーの [開始] をクリックするか、F5 を押します)。

    アプリケーションが読み込みを完了すると、診断ツールの概要ビューが表示されます。

    Screenshot of Diagnostics Tools Summary Tab.

    Note

    メモリ データの収集はネイティブ アプリや混在モードのアプリのパフォーマンスに影響する可能性があるため、既定でメモリのスナップショットは無効になっています。 ネイティブ アプリや混在モードのアプリのスナップショットを有効にするには、デバッグ セッションを開始します (ショートカット キー: F5)。 [診断ツール] ウィンドウが表示されたら、 [メモリ使用量] タブ、 [ヒープのプロファイル] の順に選択します。

    Screenshot of Enable snapshots.

    デバッグを停止 (ショートカット キー: Shift+F5) してから再開します。

    Screenshot of Diagnostics Tools Summary Tab.

    Note

    メモリ データの収集はネイティブ アプリや混在モードのアプリのパフォーマンスに影響する可能性があるため、既定でメモリのスナップショットは無効になっています。 ネイティブ アプリや混在モードのアプリのスナップショットを有効にするには、デバッグ セッションを開始します (ショートカット キー: F5)。 [診断ツール] ウィンドウが表示されたら、 [メモリ使用量] タブ、 [ヒープのプロファイル] の順に選択します。

    Screenshot of Enable snapshots.

    デバッグを停止 (ショートカット キー: Shift+F5) してから再開します。

  6. デバッグ セッションの開始時にスナップショットを取得するには、 [メモリ使用量] 概要ツールバーで [スナップショットの取得] を選択します。 (ここにもブレークポイントを設定すると役に立つ場合があります。)

    Screenshot of Take Snapshot button.

    Screenshot of Take Snapshot button.

    ヒント

    メモリ比較のベースラインを作成するには、デバッグ セッションの開始時に、スナップショットを取得することを検討します。

  7. 最初のブレークポイントにヒットするシナリオを実行します。

  8. デバッガーが最初のブレークポイントで一時停止している間に、 [メモリ使用量] 概要ツールバーで [スナップショットの取得] を選択します。

  9. F5 キーを押すと、アプリケーションが 2 つ目のブレークポイントまで実行されます。

  10. 次に、別のスナップショットを取得しましょう。

    この時点で、データの分析を開始できます。

    データの収集または表示に問題がある場合は、「プロファイル エラーのトラブルシューティングと問題の修正」をご覧ください。

メモリ使用量データの分析

メモリ使用量の概要テーブルの行では、デバッグ セッション中に取得したスナップショットが一覧表示され、より詳細なビューへのリンクが提供されています。

Screenshot of Memory Usage table.

Screenshot of Memory Usage table.

列の名前は、プロジェクトのプロパティで選択した次のデバッグ モードによって決まります: .NET、ネイティブ、混合 (.NET とネイティブの両方)。

  • [オブジェクト (相違)] および [割り当て (相違)] 列には、スナップショット取得時の .NET メモリおよびネイティブ メモリ内のオブジェクト数が表示されます。

  • [ヒープ サイズ (相違)] 列には、.NET ヒープおよびネイティブ ヒープのバイト数が表示されます。

複数のスナップショットを取得した場合、概要テーブルのセルには、行のスナップショットと前のスナップショットの間の値の変化が含まれます。

メモリ使用量を分析するには、リンクを 1 つクリックして、メモリ使用量の詳細なレポートを開きます。

  • 現在のスナップショットと前のスナップショットの相違点の詳細を表示するには、矢印の左にある変更リンクを選択します (Memory Usage Increase)。 赤い矢印はメモリ使用量が増加したことを示し、緑の矢印は減少したことを示しています。

ヒント

より迅速にメモリの問題を識別するために、Diff レポートは、総数が最も増加したオブジェクト型の順 ( [オブジェクト (相違)] 列の変更リンクをクリック) や、総ヒープ サイズが最も増加したオブジェクト型の順 ( [ヒープ サイズ (相違)] 変更リンクをクリック) に並べ替えられています。

  • 選択したスナップショットのみの詳細を表示するには、変更リンクではないリンクをクリックします。

    レポートが新しいウィンドウに表示されます。

マネージド型レポート

メモリ使用量の概要テーブルで、 [オブジェクト (相違)] または [割り当て (相違)] セルの現在のリンクを選択します。

Screenshot of managed type report.

Screenshot of managed type report.

上のウィンドウには、型で参照されているすべてのオブジェクトのサイズ (包括サイズ) を含む、スナップショット内の型の総数およびサイズが表示されます。

下のウィンドウの [ルートのパス] ツリーには、上ウィンドウで選択されている型を参照するオブジェクトが表示されます。 オブジェクトを参照する最後の型が解放されたときにのみ、.NET のガベージ コレクターによってそのメモリがクリーンアップされます。

[参照されるオブジェクト] ツリーには、上のウィンドウで選択されている型に保持されている参照が表示されます。

Screenshot of Referenced Objects report.

[参照される型] ツリーには、上のウィンドウで選択されている型に保持されている参照が表示されます。

Screenshot of Referenced Objects report.

上のウィンドウで選択した型のインスタンスを表示するには、[オブジェクト型] の横にある [インスタンスの表示] をクリックします。

Screenshot of the Instances view in the Memory Usage tool.

Screenshot of the Instances view in the Memory Usage tool.

[インスタンス] ビューには、上のウィンドウのスナップショットで選択されているインスタンスが表示されます。 [ルートのパス] および [参照されたオブジェクト] ウィンドウには、選択したインスタンスを参照するオブジェクト、および選択したインスタンスで参照される型が表示されます。 スナップショットが取得された時点でデバッガーを停止すると、 [値] セルの上にマウス カーソルを移動して、ツール ヒントにオブジェクトの値を表示することができます。

ネイティブ型のレポート

[診断ツール] ウィンドウのメモリ使用量の概要テーブルで、 [割り当て (相違)] または [ヒープ サイズ (相違)] セルの現在のリンクを選択します。

Screenshot of Native Type View.

Screenshot of Native Type View.

[型ビュー] には、スナップショットの型の数およびサイズが表示されます。

  • 選択した型のインスタンス アイコン (The instance icon in the Object Type column) を選択し、スナップショットの選択した型のオブジェクトに関する情報を表示します。

    [インスタンス] ビューには、選択した型の各インスタンスが表示されます。 インスタンスを選択すると呼び出し履歴が表示され、その結果、 [割り当て呼び出し履歴] ウィンドウにそのインスタンスが作成されます。

    Screenshot of the Instances view and Allocation Call Stack pane.

  • 選択した型の横にある [インスタンスの表示] を選択し、スナップショットの選択した型のオブジェクトに関する情報を表示します。

    [インスタンス] ビューには、選択した型の各インスタンスが表示されます。 インスタンスを選択すると呼び出し履歴が表示され、その結果、 [割り当て呼び出し履歴] ウィンドウにそのインスタンスが作成されます。

    Screenshot of the Instances view and Allocation Call Stack pane.

  • [表示モード] の一覧で [スタック ビュー] を選択し、選択した型の割り当て履歴を表示します。

    Screenshot of Stacks view.

  • [スタック] を選択して、選択した型の割り当て履歴を表示します。

    Screenshot of Stacks view.

メモリ使用量分析情報

メモリ分析ツールは、マネージド メモリに関して、いくつかの強力な組み込みの自動分析情報も提供します。 マネージド型レポートで [分析情報] タブを選択すると、[重複文字列][スパース配列][イベント ハンドラー リーク] などの適用可能な自動分析情報が表示されます。

Screenshot of the insight view in the Memory Usage tool.

[重複文字列] セクションには、ヒープ上に複数回割り当てられる文字列の一覧が表示されます。 さらに、このセクションには、無駄なメモリの合計、つまり文字列のサイズに (インスタンスの数 - 1) を掛けたものが表示されます。

[スパース配列] セクションには、大部分が 0 の要素で埋められている配列が表示されます。これはパフォーマンスとメモリ使用量の観点から非効率的である可能性があります。 メモリ分析ツールは、このような配列を自動的に検出し、ゼロ値のために無駄になっているメモリの量を示します。

Visual Studio 2022 バージョン 17.9 Preview 1 で利用できる [イベント ハンドラー リーク] セクションには、あるオブジェクトが別のオブジェクトのイベントをサブスクライブするときに発生する可能性のあるメモリ リークが表示されます。 イベントのパブリッシャーがサブスクライバーより長く存在した場合、サブスクライバーへの他の参照がない場合でもサブスクライバーは存続します。 これが起こるとメモリ リークが発生し、使用されていないメモリが適切に解放されず、時間の経過と共にアプリケーションで使用されるメモリが増えていきます。

特定の型には、それが保持しているネイティブ メモリのサイズを決定するために読み取ることができるフィールドが存在することが知られています。 [分析情報] タブでは、オブジェクト グラフにフェイク ネイティブ メモリ ノードが表示されます。これらは、UI がこれらを認識してそのサイズと参照グラフを表示できるように、その親オブジェクトによって保持されています。

Screenshot of the native insight view in the Memory Usage tool.

変更 (Diff) レポート

  • [診断ツール] ウィンドウの [メモリ使用量] タブで、概要テーブルのセルにある変更リンクを選択します。

    Screenshot of Choose a change link in a cell.

    Screenshot of Choose a change link in a cell.

  • マネージド レポート、もしくはネイティブ レポートの [比較対象] 一覧でスナップショットを選択します。

    Screenshot of Choose a snapshot from the Compare To list.

    Screenshot of Choose a snapshot from the Compare with list.

変更レポートを実行すると、基本のスナップショット値と比較のスナップショットの差分を表示する列 ( (Diff) のマークが付けられる) が、基本レポートに追加されます。 ネイティブ型の差分レポート ビューは次のようになります。

Screenshot of Native Types Diff View.

Screenshot of Native Types Diff View.

ブログとビデオ

デバッグ中に CPU とメモリを分析する

Visual C++ ブログ:Visual C++ 2015 のメモリ プロファイル

次の手順

このチュートリアルでは、メモリ使用量データを収集し、分析する方法について学習しました。 プロファイラーのツアーを既に完了している場合は、プロファイル ツールを使用してコードを最適化するための一般的なアプローチについて知る必要があります。

このチュートリアルでは、デバッグ中にメモリ使用率データを収集して分析する方法を学習しました。 パフォーマンス プロファイラーを使用したリリース ビルドでのメモリ使用率の分析の詳細を確認できます。