web-dev-qa-db-ja.com

C#アプリケーションでメモリ不足例外が発生するのはなぜですか?

私のメモリは物理4Gですが、1.5Gメモリオブジェクトだけを作成してもメモリ例外が発生したのはなぜですか。なぜアイデアがありますか? (同時に、タスクマネージャーの[パフォーマンス]タブでメモリがいっぱいになっておらず、ここに入力することもできました。したがって、実際にはメモリが少なくないため、他のメモリ制限にぶつかると思います)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestBigMemoryv1
{
    class MemoryHolderFoo
    {
        static Random seed = new Random();
        public Int32 holder1;
        public Int32 holder2;
        public Int64 holder3;

        public MemoryHolderFoo()
        {
            // prevent from optimized out
            holder1 = (Int32)seed.NextDouble();
            holder2 = (Int32)seed.NextDouble();
            holder3 = (Int64)seed.NextDouble();
        }
    }

    class Program
    {
        static int MemoryThreshold = 1500; //M
        static void Main(string[] args)
        {
            int persize = 16;
            int number = MemoryThreshold * 1000 * 1000/ persize;
            MemoryHolderFoo[] pool = new MemoryHolderFoo[number];
            for (int i = 0; i < number; i++)
            {
                pool[i] = new MemoryHolderFoo();
                if (i % 10000 == 0)
                {
                    Console.Write(".");
                }
            }

            return;
        }
    }
}
29
George2

通常の32ビットWindowsアプリでは、プロセスのアドレス可能なメモリは2GBのみです。これは、使用可能な物理メモリの量とは無関係です。

したがって、2GBが使用可能ですが、1.5が割り当て可能です。重要なのは、コードがプロセスで実行されている唯一のコードではないということです。他の.5 GBは、おそらくCLRとプロセス内のフラグメンテーションです。

Update:64ビットプロセスの.NET 4.5では、 gcAllowVeryLargeObjects 設定が有効になっている場合、大きな配列を使用できます。

64ビットプラットフォームでは、合計サイズが2ギガバイト(GB)を超えるアレイを有効にします。配列内の要素の最大数はUInt32.MaxValueです。

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
40
JaredPar

他のポイントに追加するだけです。ダーティな量のメモリにアクセスしたい場合は、x64を考慮してください-ただし、最大singleオブジェクトサイズはまだ2GBであることに注意してください。また、x64では参照が大きくなるため、実際には、参照タイプのsmaller最大配列/リストサイズが取得されます。もちろん、その限界に達するまでに、おそらくとにかく間違ったことをしているでしょう!

他のオプション:

  • ファイルを使用する
  • データベースを使用する

(明らかに、両方ともインプロセスメモリと比較してパフォーマンスの違いがあります)


更新:4.5より前のバージョンの.NETでは、最大オブジェクトサイズは2GBです。 4.5以降では、 gcAllowVeryLargeObjects が有効になっている場合、より大きなオブジェクトを割り当てることができます。 stringの制限は影響を受けませんが、リストは配列によって支えられているため、「配列」も「リスト」をカバーする必要があります。

10
Marc Gravell

前の返信に追加するだけです。/3Gb[およびオプションでuserva]ブートフラグでブートされたシステムの2Gb制限を超えることができます。

5
KristoferA

Visual Studioのデフォルトのコンパイルモードである32ビットプロセスではなく、64ビットプロセスをビルドしていることを確認してください。これを行うには、プロジェクトを右クリックし、[プロパティ]-> [ビルド]-> [プラットフォームターゲット:x64]を選択します。 32ビットプロセスと同様に、32ビットでコンパイルされたVisual Studioアプリケーションには2GBの仮想メモリ制限があります。

各プロセスには、アドレス空間と呼ばれる独自の仮想メモリがあり、実行するコードと操作するデータをマッピングします。 32ビットプロセスは32ビット仮想メモリアドレスポインターを使用します。これにより、32ビットプロセスがアドレス指定できる仮想メモリの量に対して4GB(2 ^ 32)の絶対的な上限が作成されます。ただし、オペレーティングシステムはその半分を必要とし(独自のコードとデータを参照するため)、プロセスごとに2GBの制限を作成します。 32ビットアプリケーションが2GBを超えるアドレス空間を消費しようとすると、コンピューターの物理メモリがいっぱいではない場合でも、「System.OutOfMemory」が返されます。

64ビットプロセスは64ビットポインターを使用するため、この制限はありません。したがって、理論上の最大アドレス空間は16エクサバイト(2 ^ 64)です。実際には、Windows x64はプロセスの仮想メモリを8TBに制限しています。メモリ制限の問題の解決策は、64ビットでコンパイルすることです。

ただし、Visual Studioでのオブジェクトのサイズはデフォルトで2GBに制限されています。合計サイズが2GBを超える複数のアレイを作成できますが、デフォルトで2GBを超えるアレイを作成することはできません。 2GBを超える配列を作成したい場合は、app.configファイルに次のコードを追加してください。

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
5

他のポスターが述べたように、32ビットアプリとして最大2Gbのアドレス可能なメモリを持っています。オーバーヘッドを忘れないでください。 93百万個のオブジェクトの配列を作成しています-オブジェクトごとに4バイトのオーバーヘッドが発生すると、350 MBのメモリが追加されます。

4
geofftnz

知っておくべきもう一つのこと;一部の.NETオブジェクトには「連続」メモリが必要です。すなわち、大きな配列を割り当てようとしている場合、システムはプロセスに十分な空きメモリだけでなく、そのすべての空きメモリが1つの大きなチャンクになる必要がある場合があります。利用できません。

一部のオブジェクト/データ型にはこの要件があり、一部はそうではありません...どの要件があるのか​​覚えていませんが、StringBuilderとMemoryStreamの要件が異なることを思い出すようです。

4
Yort

32ビットWindowsオペレーティングシステムでは、1つのアプリケーションがアクセスできる最大の「ユーザーモード」メモリは2GBです...ボックスに4GBのメモリがあると仮定します。

WindowsサーバーでのアンマネージVC++アプリケーションのメモリ消費

http://blogs.technet.com/markrussinovich/archive/2008/07/21/3092070.aspx

(昨日、私がほとんど同じことを尋ねたので、あなたがこれを尋ねたのは面白いです...)

3
uzbones