10

十分なメモリがあるかどうかを確認するために、次のコードを作成しました。

while (true)
{
    try
    {
        // Check for available memory.
        memFailPoint = new MemoryFailPoint(250);

        break;
    }
    catch (InsufficientMemoryException ex)
    {
        if (memFailPoint != null)
        {
          memFailPoint.Dispose();
        }

        Thread.Sleep(waitSecond * 1000);
    }
}

Windows 7 64 ビット マシンのコンソール アプリケーションで上記を実行しています。

このメソッドには、10 秒ごとに 4 回の呼び出しがあります。

最初は問題なく動作しますが、2 ~ 3 時間後には常にInsufficientMemoryExceptionスローされます。使用可能なメモリを確認したところ、1 GB を超えています。

私は多くのことを試しましたが、なぜこれが起こっているのかを見つけることができませんでした.

以下はスタック トレースです。

at System.Runtime.MemoryFailPoint..ctor(Int32 sizeInMegabytes)
at SocketListner.AcceptConnection(IAsyncResult res) in H:\Projects\SocketListner.cs:line 308

内部例外はありません。

4

3 に答える 3

16

このメソッドが正しく機能していることを信頼できます。250 メガバイトを要求すると、この例外は 32 ビット プロセスでトリップする可能性が非常に高くなります。プログラムがしばらく実行されていると、それを取得するのが難しくなります。

使用可能なすべての仮想メモリ アドレス空間を消費したため、プログラムが OOM でクラッシュすることはありません。割り当てに十分な大きさの穴がアドレス空間に残っていないため、クラッシュします。あなたのコードは、一気に 250 メガバイトを割り当てるのに十分な大きさのホールを要求します。例外が発生しない場合は、この割り当てが失敗しないことを確認できます。

しかし、250 メガバイトはかなり多く、これは非常に大きな配列です。また、「アドレス空間の断片化」と呼ばれる問題が原因で失敗する可能性が非常に高くなります。つまり、プログラムは通常、いくつかの非常に大きな穴 (最大で約 600 メガバイト) から始まります。.NET ランタイムとアンマネージ Windows DLL によって使用されるコードとデータを格納するために行われた割り当ての間に利用可能なホール。プログラムがより多くのメモリを割り当てると、これらの穴は小さくなります。いくらかのメモリを解放する可能性がありますが、それは大きな穴を再現しません. 通常、元のサイズの約半分の2 つの穴があり、元の大きな穴を 2 つに分割する中央のどこかに割り当てられます。

これはフラグメンテーションと呼ばれます。多くのメモリを割り当てて解放する 32 ビット プロセスは、最終的に仮想メモリ アドレス空間を断片化するため、しばらくすると利用可能な最大のホールが小さくなり、約 90 メガバイトがかなり一般的です。250 メガバイトを要求すると、ほぼ確実に失敗します。より低い目標を設定する必要があります。

250 メガバイトまでの割り当ての合計が確実に機能することが保証されているため、別の方法で機能することを期待していたことは間違いありません。ただし、これは MemoryFailPoint がどのように機能するかではなく、可能な限り最大の割り当てをチェックするだけです。言うまでもなく、これはあまり役に立ちません。それ以外の場合は、.NET フレームワークのプログラマーに同情します。私たちが望むように動作させるには費用がかかり、割り当てのサイズが最も重要であるため、実際には保証を提供できません。

仮想メモリは、信じられないほど安価な豊富なリソースです。しかし、それをすべて消費することに近づくのは非常に面倒です。ギガバイトを消費すると、OOM がランダムにストライクする可能性が高くなり始めます。この問題の簡単な修正を忘れないでください。64 ビットのオペレーティング システムを実行しています。したがって、EXE プラットフォーム ターゲットを AnyCPU に変更するだけで、大量の仮想アドレス空間が得られます。OS のエディションによって異なりますが、1 テラバイトが可能です。まだ断片化していますが、もう気にする必要はありません。穴は巨大です。

最後になりましたが、コメントに表示されているように、この問題はRAM とは何の関係もありません。仮想メモリは、使用している RAM の量とはまったく関係ありません。仮想メモリ アドレスを RAM 内の物理アドレスにマップするのは、オペレーティング システムの仕事であり、動的に行われます。メモリの場所にアクセスすると、ページ フォールトが発生する可能性があり、OS がそのページに RAM を割り当てます。逆に、OS はページの RAM を別の場所で必要なときにマップ解除します。RAM が不足することは決してありません。それが発生する前に、マシンの速度が遅くなります。SysInternals の VMMap ユーティリティは、プログラムの仮想アドレス空間がどのように見えるかを確認するのに役立ちますが、大規模なプロセスの情報に夢中になる傾向があります。

于 2013-06-11T14:53:46.857 に答える
0

GC.GetTotalMemoryメソッドを使用して、呼び出しの前後で使用可能なメモリ量を判断することを検討してください。

memFailPoint = new MemoryFailPoint(250);

InsufficientMemoryExceptionMemoryFailPoint操作を開始する前に、現在使用可能なメモリの量よりも大きい投影メモリ割り当てを指定すると、コンストラクターによってスローされます。user7116がコメントしたように、最初に確認する必要があるのはそのためです。

このリンクの例は、解決策を提供するはずです: MemoryFailPoint クラス

次のmsdn ブログ記事を確認することもできます:メモリ不足ですか? プログラムで使用できるメモリを増やす簡単な方法

于 2013-06-11T14:36:51.103 に答える
0

ここに記載されているように、MemoryFailPoint は連続して使用可能なメモリをチェックします: http://msdn.microsoft.com/fr-fr/library/system.runtime.memoryfailpoint.aspx

メモリの消費量は非常に少ないかもしれませんが、大量に断片化しているため、必要なサイズの連続したメモリ ブロックを割り当てることができません。この問題は、数時間後に発生するのが非常に一般的です。これを回避するには、インスタンス化し続けるオブジェクトにオブジェクトのプールを使用します。これにより、使用中のメモリ空間がより固定されます。

于 2013-06-11T14:55:46.963 に答える