この記事では、デプロイされた ASP.NET Core サーバー側の Blazor アプリでメモリ使用量を管理する方法について説明します。
サーバーでは、ユーザー セッションごとに新しい回線が作成されます。 各ユーザー セッションは、ブラウザーでの 1 つのドキュメントのレンダリングに対応します。 たとえば、複数のタブで複数のセッションが作成されます。
Blazor は、セッションを開始した 回線と呼ばれるブラウザーへの一定の接続を維持します。 ユーザーがネットワーク接続を失ったり、ブラウザーを突然閉じたりした場合など、いくつかの理由でいつでも接続が失われる可能性があります。 接続が失われた場合、 Blazor には、"切断された" プールに限られた数の回線を配置する復旧メカニズムがあり、クライアントが再接続してセッションを再確立する時間が制限されます (既定: 3 分)。
その後、 Blazor は回線を解放し、セッションを破棄します。 その時点から、回路はガベージコレクション (GC) の対象となり、回路のGC世代のコレクションがトリガーされたときに回収されます。 理解すべき重要な側面の 1 つは、回線の有効期間が長いことです。つまり、回線によってルート化されたオブジェクトのほとんどは、最終的に Gen 2 に到達します。 その結果、Gen 2 コレクションが発生するまで、これらのオブジェクトが解放されない可能性があります。
メモリ使用量の一般的な測定
前提条件:
- アプリは リリース 構成で発行する必要があります。 生成 されたコードは運用環境のデプロイに使用されるコードを表していないので、デバッグ構成の測定値は関係ありません。
- デバッガーをアタッチせずにアプリを実行する必要があります。これは、アプリの動作にも影響し、結果が損なわれる可能性があるためです。 Visual Studio で、メニュー バーから [デバッグ] [>なしで開始] を選択するか、キーボードを使用して Ctrl+ キーを押して、デバッグなしでアプリを起動します。
- .NET によって実際に使用されるメモリの量を理解するには、さまざまな種類のメモリを検討してください。 一般に、開発者は Windows OS 上のタスク マネージャーでアプリのメモリ使用量を調べます。通常は、使用中の実際のメモリの上限が提供されます。 詳細については、次の記事を参照してください。
- .NET メモリ パフォーマンス分析: 特に、 メモリの基礎に関するセクションを参照してください。
- メモリ パフォーマンスの問題の診断のワークフロー (3 部構成) : シリーズの 3 つの記事へのリンクは、シリーズの各記事の先頭にあります。
メモリ使用量がBlazorに適用されました
blazor によって使用されるメモリを次のように計算します。
(アクティブ回路 × 回路ごとのメモリ) + (切断された回路 × 回路ごとのメモリ)
回線が使用するメモリの量と、アプリが維持できる最大アクティブ回線は、アプリの書き込み方法によって大きく異なります。 可能なアクティブ回路の最大数は、大まかに次のように記述されます。
使用可能な最大メモリ / 回線ごとのメモリ = 潜在的な最大アクティブ回路数
Blazorでメモリ リークが発生するには、次のことが必要です。
- メモリは、アプリではなくフレームワークによって割り当てられる必要があります。 アプリに 1 GB の配列を割り当てる場合、アプリは配列の破棄を管理する必要があります。
- メモリはアクティブに使用しないでください。つまり、回線はアクティブではなく、切断された回線キャッシュから削除されています。 アクティブな回線の最大数が実行されている場合、メモリ不足は、メモリ リークではなく、スケールの問題です。
- 回線の GC 生成用のガベージ コレクション (GC) が実行されましたが、フレームワーク内の別のオブジェクトが回線への強力な参照を保持しているため、ガベージ コレクターは回線を要求できませんでした。
それ以外の場合は、メモリ リークはありません。 回線がアクティブ (接続または切断) の場合、回線はまだ使用中です。
回路の GC 世代のコレクションが実行されないと、ガベージコレクターがそのタイミングでメモリを解放する必要がないため、メモリは解放されません。
GC 生成のコレクションが実行され、回線が解放される場合は、プロセスではなく GC 統計に対してメモリを検証する必要があります。これは、.NET が仮想メモリをアクティブな状態に保つことを決定する場合があるためです。
メモリが解放されていない場合は、アクティブまたは切断されていない回線が見つかり、フレームワーク内の別のオブジェクトによってルート化されている必要があります。 それ以外の場合、メモリを解放できないことは、開発者コードのアプリの問題です。
メモリ使用量の削減
アプリのメモリ使用量を減らすには、次のいずれかの方法を採用します。
- リリース構成でアプリを発行します。
- 発行済みのバージョンのアプリを実行します。
- 実行中のアプリにデバッガーをアタッチしないでください。
- Gen 2 を強制的にトリガーしてコレクションを圧縮することで、メモリが解放されますか?
- アプリが大きなオブジェクト ヒープにオブジェクトを割り当てるかどうかを検討します。
- アプリが要求と処理でウォームアップされた後、メモリの増加をテストしていますか? 通常、アプリのフットプリントに一定のメモリを追加するコードの初回実行時に設定されるキャッシュがあります。
- .NET プロセスで使用されるメモリの合計量を制限します。 詳細については、 ガベージ コレクションのランタイム構成オプションを参照してください。
- 切断された回線の数を減らします。
- 回線が切断状態になる時間を短縮します。
- ガベージコレクションを手動で開始して、システムが停止している間にコレクションを実行します。
- サーバー モードではなく、ガベージ コレクションを積極的にトリガーするワークステーション モードでガベージ コレクションを構成します。
一部のモバイル デバイス ブラウザーのヒープ サイズ
クライアント上で実行され、モバイル デバイス ブラウザー (特に iOS の Safari) を対象とする Blazor アプリをビルドする場合は、MSBuild プロパティを使用してアプリの最大メモリを減らす EmccMaximumHeapSize が必要になる場合があります。 詳しくは、「ASP.NET Core Blazor WebAssembly のホストと展開」をご覧ください。
その他のアクションと考慮事項
- メモリ要求が高い場合にプロセスのメモリ ダンプをキャプチャし、オブジェクトが最も多くのメモリを使用していて、それらのオブジェクトがルート化されている場所 (それらのオブジェクトへの参照を保持するもの) を特定します。
-
dotnet-countersを使用して、アプリ内のメモリの動作に関する統計情報を調べることができます。 詳細については、「 パフォーマンス カウンターの調査 (dotnet-counters)」を参照してください。 - GC がトリガーされた場合でも、.NET は、近い将来にメモリを再利用する可能性があるため、すぐに OS に返す代わりにメモリを保持します。 これにより、メモリのコミットとデコミットを常に行うことによって発生する高コストを避けられます。 これは、
dotnet-countersを使用すると、ガベージコレクションが発生し、使用されるメモリの量が 0 (ゼロ) に減るため反映されますが、ワーキングセットカウンターの減少は表示されません。これは、.NET がメモリを再利用するために保持している兆候です。 この動作を制御するためのプロジェクト ファイル (.csproj) 設定の詳細については、「 ガベージ コレクションのランタイム構成オプション」を参照してください。 - サーバー GC は、アプリのフリーズを回避するために絶対に必要であると判断するまでガベージ コレクションをトリガーせず、アプリがマシン上で実行されている唯一のものであり、システム内のすべてのメモリを使用できることを考慮します。 システムに 50 GB がある場合、ガベージ コレクターは、Gen 2 コレクションをトリガーする前に、使用可能なメモリの完全な 50 GB を使用することを求めます。
- 切断された回路のリテンション構成については、ASP.NET Core BlazorSignalRガイドを参照してください。
ASP.NET Core