次の方法で共有


ASP.NET でのメモリ不足の問題 (System.OutOfMemoryException) のトラブルシューティング

この記事は、ASP.NET の メモリ不足 エラーのトラブルシューティングに役立ちます。

元の製品バージョン: ASP.NET
元の KB 番号: 2020006

現象

Microsoft カスタマー サポート サービスで見られる最も一般的な問題の 1 つは、 OutOfMemoryException シナリオです。 そのため、メモリの問題の原因のトラブルシューティングと特定に役立つリソースのコレクションをまとめました。

OutOfMemoryExceptionのトラブルシューティングの詳細について説明する前に、この問題の原因を理解しておくことが重要です。 多くの開発者が信じているものとは対照的に、インストールされている RAM の量は、 OutOfMemoryExceptionの可能性には影響しません。 32 ビット オペレーティング システムは、ボックスにインストールされている物理メモリの量に関係なく、4 GB の仮想アドレス空間に対処できます。 その中で、オペレーティング システム (カーネル モード メモリ) 用に 2 GB が予約され、2 GB がユーザー モード プロセスに割り当てられます。 カーネル モード メモリに割り当てられた 2 GB はすべてのプロセス間で共有されますが、各プロセスは独自の 2 GB のユーザー モード アドレス空間を取得します。 すべて、 /3gb スイッチを有効にして実行していないことを前提としています。

アプリケーションでメモリを使用する必要がある場合は、仮想アドレス空間のチャンクを予約し、そのチャンクからメモリをコミットします。 これは、マネージド ヒープを拡張するためにメモリが必要な場合に、.NET Framework のガベージ コレクター (GC) が行うものです。 GC は、小さなオブジェクト ヒープ (85 KB 未満のオブジェクトが存在する場合) に新しいセグメントを必要とする場合、64 MB の割り当てを行います。 ラージ オブジェクト ヒープに新しいセグメントが必要な場合は、16 MB の割り当てを行います。 これらの大きな割り当ては、プロセスが処理する必要がある 2 GB のアドレス空間の連続したブロックから満たす必要があります。 オペレーティング システムが連続するメモリ ブロックに対する GC の要求を満たすことができない場合は、 System.OutOfMemoryException (OOM) が発生します。

Note

64 ビット操作システムで実行される 32 ビット プロセスは、4 GB のユーザー モード メモリに対応でき、64 ビット操作システムで実行されている 64 ビット プロセスは 8 TB のユーザー モード メモリに対処できるため、64 ビット操作システムの OOM は可能性が高くありません。 64 ビット操作システムで実行されている 32 ビット プロセスで OOM が発生する可能性がありますが、通常は、プロセスが 3 GB に近いプライベート バイトを使用するまで発生しません。

OOM 条件が表示される理由は 2 つあります。

  1. プロセスで多くのメモリが使用されています (通常、32 ビット環境では 800 MB を超えています)。
  2. 仮想アドレス空間が断片化されているため、大規模で連続した割り当てが成功する可能性が低くなります。

1 と 2 の組み合わせにより、OOM 条件を確認することもできます。 詳細については、「 ASP.NETでの System.OutOfMemoryExceptions のトラブルシューティング」を参照してください。

OOM が発生すると、次の 1 つ以上の現象が発生することがあります。

OOM 条件の原因の特定に関しては、実際には、メモリの高い状況または断片化されたアドレス空間の原因の特定に取り組んでいます。 これらの状況の考えられるすべての原因を文書化することはできませんが、定期的に発生する一般的な原因がいくつかあります。

次の情報では、OOM 条件の一般的な原因と、これらの各原因の解決に関する解決策について説明します。

文字列連結

マネージド アプリケーション (.NET Framework を使用して記述されたアプリケーション) 内の文字列は変更できません。 新しい値が文字列に割り当てられると、コピーは既存の文字列で作成されます。 そして、新しい値が新しい文字列に割り当てられます。 通常、問題は発生しません。 しかし、多数の文字列が連結されると、開発者が認識するよりも多くの文字列割り当てが発生します。 また、メモリの増加と OOM の状態につながる可能性があります。

文字列連結のために OOM を回避するには、 StringBuilder クラスを使用していることを確認します。 詳細については、「 Visual C# で文字列連結のパフォーマンスを向上させる方法」を参照してください。

マネージド ヒープ内の断片化

マネージド アプリケーションのガベージ コレクター (GC) は、断片化の量を減らすためにヒープを圧縮します。 ただし、マネージド アプリケーションでオブジェクトをピン留めすることはできます。 ピン留めされたオブジェクトは、ヒープ圧縮中に移動できません。 これを行うと、オブジェクトが配置されているアドレスが変更されます。 アプリケーションが多数のオブジェクトをピン留めしたり、オブジェクトを長時間ピン留めしたりすると、マネージド ヒープで断片化が発生する可能性があります。 GC がマネージド ヒープをより頻繁に拡大し、OOM 状態を引き起こす可能性があります。

.NET Framework 1.0 以降のピン留めにより、OOM 条件の最小化に取り組んできました。 各バージョンでは、増分的な改善が行われています。 ただし、実装できる設計パターンは、オブジェクトをピン留めする必要がある場合に役立ちます。

仮想アドレス (VA) 空間での断片化

各プロセスには一定量のメモリが割り当てられ、そのメモリはプロセスの VA 領域を表します。 VA 領域が断片化されると、GC が連続したメモリの大きなブロックを取得してマネージド ヒープを拡張できない可能性が高くなります。 そして、それはOOM状態につながる可能性があります。

VA 空間での断片化は、多くの場合、次の 1 つ以上のシナリオによって発生します。

  • 複数のアプリケーション ドメインに同じアセンブリを読み込む。

    同じアプリケーション プールで実行されている複数のアプリケーションでアセンブリを使用する必要がある場合は、アセンブリに厳密な名前を付けて GAC にインストールします。 これにより、アセンブリがプロセスに 1 回だけ読み込まれるようにします。

  • <compilation>要素のデバッグ属性をtrueに設定して、運用環境でアプリケーションを実行する。

    • 運用環境では、 <compilation> 要素のデバッグ属性を false する必要があります。
    • <deploy retail="true" />構成を使用して、製品でデバッグが常に無効になっていることを確認できます。 詳細については、「 deployment 要素 (ASP.NET 設定スキーマ)」を参照してください。
  • eXtensible スタイル シート言語 (XSL) 変換内でのスクリプトの使用、または XmlSerializersの作成。

    この場合、拡張スタイル シート言語変換 (XSLT) スクリプトまたは XmlSerializersによって発生する動的アセンブリ。

大量のデータ セットを返す

データベースまたはその他のデータ ソースのデータを使用する場合は、返されるデータの量を制限することが重要です。 たとえば、必要なときにデータベースからデータの一部を取得するコストを回避するために、データベース テーブル全体を返すクエリ結果をキャッシュすることは、適切なアプローチではありません。 これにより、メモリが高く、OOM 状態につながる可能性があります。 ユーザーが同様のクエリを開始できるようにすることも、メモリの高い状況を作成するもう 1 つの一般的な方法です。 たとえば、テキサス州の会社のすべての従業員、またはテキサス州のすべての顧客を返します。姓は文字 S で始まります。

データベースから返すことができるデータの量は常に制限してください。 ページに表示されるデータの量を制御できないため、 SELECT * FROM. . . などのクエリを許可しないでください。

GridView コントロールなどの UI 要素で大きなデータ結果を表示しないようにすることも同様に重要です。 返されるデータに必要なメモリに加えて、結果をレンダリングするために必要な文字列や UI 要素で大量のデータを使用します。 ページングを実装し、大量のデータ セットが返されないように入力を検証することで、この問題を回避できます。

トレースが有効になっている運用環境で実行する

ASP.NET トレースは、アプリケーションをトラブルシューティングするための強力な機能です。 ただし、運用環境に残すべきではありません。 トレース ASP.NET DataTables などのデータ構造を使用してトレース情報を格納し、時間の経過と同時に、OOM につながる可能性のある高いメモリ状態を引き起こす可能性があります。

運用環境ではトレースを無効にする必要があります。 これを行うには、web.config ファイルで、<trace>要素のenabled属性を false に設定します。 <deploy retail="true" />を使用してリテール展開を有効にすると、アプリケーションでのトレースも無効になります。

ネイティブ リソースのリーク

多くのマネージド リソースでは、ネイティブ リソースも利用されます。 GC はネイティブ リソースをクリーンアップしないため、開発者は Dispose メソッドを実装して呼び出してネイティブ リソースをクリーンアップする必要があります。 IDisposable インターフェイスを実装する型を使用していて、Dispose メソッドを呼び出さない場合は、ネイティブ リソースがリークし、OOM 状態が発生するリスクがあります。

これらのオブジェクトは、 iDisposable インターフェイスを実装する必要があります。不要になったら、これらのオブジェクトに対して Dispose メソッドを呼び出す必要があります。