2

問題は、.net での配列の割り当てに関するものです。以下に、取得できる配列の最大長が長さになるサンプル プログラムがあります。長さを +1 に増やすと、outofMemory 例外が発生します。しかし、長さを維持してコメントを削除すると、2 つの異なる大きな配列を割り当てることができます。両方の配列は、2 GB の .net 許容オブジェクト サイズよりも小さく、合計メモリも仮想メモリよりも小さくなっています。誰かが何か考えを入れることができますか?

 
class Program
    {
        static int length = 203423225;
        static double[] d = new double[length];
        //static int[] i = new int[15000000];
        static void Main(string[] args)
        {

            Console.WriteLine((sizeof(double)*(double)length)/(1024*1024));

            Console.WriteLine(d.Length);
            //Console.WriteLine(i.Length);
            Console.WriteLine(Process.GetCurrentProcess().VirtualMemorySize64.ToString());
        }
    }
4

2 に答える 2

8

32 ビット プロセスは、使用可能なアドレス空間から配列に仮想メモリを割り当てる必要があります。デフォルトでは 2 ギガバイトです。コードとデータの両方が混在しています。割り当ては、既存の割り当て間の穴から行われます。

このような割り当ては、仮想メモリが残っていないために常に失敗するのではなく、使用可能なホールが十分に大きくないために失敗します。そして、大きな穴を求めて、1.6 ジガバイトを取得することは非常にまれであり、追加の DLL をロードしない非常に単純なプログラムでのみ機能します。不十分なベースの DLL は、大きな穴を 2 つにカットする良い方法であり、そのような割り当てが成功する可能性を大幅に減らします。より一般的な初回試行時の割り当ては、約 650 メガバイトです。使用可能な別の穴があったため、2 番目の割り当ては失敗しませんでした。プログラムがしばらく実行され、アドレス空間が断片化されると、オッズは大幅に低下します。90 MB の割り当ては失敗する可能性があります。

SysInternals の VMMap ユーティリティを使用すると、プログラムの仮想メモリ アドレス空間がどのように切り分けられるかを把握できます。

簡単な回避策は、EXE プロジェクトのプラットフォーム ターゲット設定を AnyCPU に設定し、64 ビット オペレーティング システムでプログラムを実行することです。アドレス指定可能な仮想メモリ スペースを大量に利用できます。制限されるのは、ページング ファイルの最大許容サイズと .NET の 2 ギガバイト オブジェクト サイズ制限だけです。<gcAllowVeryLargeObjects>新しいconfig 要素を使用して .NET 4.5 で対処されている制限。32 ビット プログラムでも、editbin.exe の /LARGEADDRESSAWARE オプションを使用して、64 ビット オペレーティング システムで利用可能な 4 ギガバイトの 32 ビット アドレス空間を利用できます。これは、ビルド後のイベントで実行する必要があります。

于 2013-02-12T14:52:38.053 に答える
0

これは、配列にメモリを割り当てる場合、メモリが連続している必要があるためです (つまり、配列はメモリの 1 つの大きなブロックとして割り当てられる必要があります)。配列を割り当てるのに十分な合計スペースがある場合でも、空きアドレス空間が分割されている場合、それらの空きスペースの最大が配列全体に対して十分な大きさでない限り、メモリの割り当ては失敗します。

于 2013-02-12T13:34:56.160 に答える