Почему я получаю исключение нехватки памяти в моем приложении C#?

У меня физическая память 4G, но почему я получил исключение из памяти, даже если я создаю объект памяти размером всего 1,5 ГБ. Есть идеи, почему? (В то же время я видел, что на вкладке производительности диспетчера задач память не полностью занята, и я также мог ввести здесь - так что памяти на самом деле не мало, поэтому я думаю, что я столкнулся с некоторыми другими ограничениями памяти)?

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;
        }
    }
}

Ответов (7)

Решение

В обычном 32-битном приложении Windows процесс имеет только 2 ГБ адресуемой памяти. Это не имеет отношения к доступному объему физической памяти.

Итак, доступно 2 ГБ, но 1,5 - это максимум, который вы можете выделить. Ключ в том, что ваш код - не единственный код, выполняющийся в процессе. Другой 0,5 ГБ - это, вероятно, среда CLR плюс фрагментация в процессе.

Обновление: в .Net 4.5 в 64-битном процессе вы можете иметь большие массивы, если включен параметр gcAllowVeryLargeObjects :

На 64-битных платформах включает массивы, общий размер которых превышает 2 гигабайта (ГБ). Максимальное количество элементов в массиве - UInt32.MaxValue.

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

Просто чтобы добавить к предыдущим ответам: вы можете выйти за пределы лимита в 2 ГБ в системах, загружаемых с загрузочными флагами / 3Gb [и, возможно, userva].

Убедитесь, что вы создаете 64-битный процесс, а не 32-битный, который является режимом компиляции Visual Studio по умолчанию. Для этого щелкните свой проект правой кнопкой мыши, выберите «Свойства» -> «Сборка» -> цель платформы: x64. Как и любой 32-разрядный процесс, приложения Visual Studio, скомпилированные в 32-разрядном режиме, имеют ограничение виртуальной памяти в 2 ГБ.

Каждый процесс имеет свою собственную виртуальную память, называемую адресным пространством, в которую он отображает код, который он выполняет, и данные, которыми он манипулирует. 32-разрядный процесс использует 32-разрядные указатели адресов виртуальной памяти, что создает абсолютный верхний предел в 4 ГБ (2 ^ 32) для объема виртуальной памяти, которую может адресовать 32-разрядный процесс. Однако операционной системе требуется половина его (для ссылки на собственный код и данные), создавая ограничение в 2 ГБ для каждого процесса. Если ваше 32-разрядное приложение пытается использовать более 2 ГБ своего адресного пространства, оно вернет «System.OutOfMemory», даже если физическая память вашего компьютера не заполнена.

64-битные процессы не имеют этого ограничения, поскольку они используют 64-битные указатели, поэтому их теоретическое максимальное адресное пространство составляет 16 эксабайт (2 ^ 64). На самом деле Windows x64 ограничивает виртуальную память процессов до 8 ТБ. Решением проблемы ограничения памяти является 64-разрядная компиляция.

Однако размер объекта в Visual Studio по-прежнему ограничен 2 ГБ. Вы сможете создать несколько массивов, общий размер которых будет больше 2 ГБ, но по умолчанию вы не можете создавать массивы размером более 2 ГБ. Надеюсь, если вы все еще хотите создавать массивы размером более 2 ГБ, вы можете сделать это, добавив следующий код в файл app.config:

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

В 32-разрядной операционной системе Windows максимальная память «пользовательского режима», к которой может получить доступ одно приложение, составляет 2 ГБ ... при условии, что у вас есть 4 ГБ памяти на коробке.

Потребление памяти неуправляемым приложением VC++ на сервере Windows

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

(Забавно, что вы спросили об этом, потому что вчера я спросил почти то же самое ...)

У вас есть максимум 2 ГБ адресуемой памяти в качестве 32-битного приложения, как упоминалось в других плакатах. Не забывайте об накладных расходах. Вы создаете массив из 93 миллионов объектов - если на каждый объект приходится 4 байта накладных расходов, это дополнительные 350 МБ памяти.

Просто в дополнение к другим пунктам; если вам нужен доступ к грязному объему памяти, рассмотрите вариант x64, но имейте в виду, что максимальный размер одного объекта по-прежнему составляет 2 ГБ. И поскольку ссылки больше в x64, это означает, что вы фактически получаете меньший максимальный размер массива / списка для ссылочных типов. Конечно, к тому времени, когда вы достигнете этого предела, вы, вероятно, все равно будете делать что-то не так!

Другие варианты:

  • использовать файлы
  • использовать базу данных

(очевидно, что оба имеют разницу в производительности по сравнению с внутренней памятью)


Обновление: в версиях .NET до 4.5 максимальный размер объекта составляет 2 ГБ. Начиная с версии 4.5 вы можете выделять более крупные объекты, если включен gcAllowVeryLargeObjects . Обратите внимание, что ограничение для string не изменяется, но «массивы» должны охватывать и «списки», поскольку списки поддерживаются массивами.

Еще одна вещь, о которой нужно знать; некоторые объекты .NET требуют «непрерывной» памяти. то есть, если вы пытаетесь выделить большой массив, системе может потребоваться не только достаточно свободной памяти в вашем процессе, но и для того, чтобы вся эта свободная память находилась в одном большом фрагменте ... и, к сожалению, память процесса со временем фрагментируется, поэтому это может не будет в наличии.

Некоторые объекты / типы данных имеют это требование, а некоторые нет ... Я не могу вспомнить, какие именно, но я, кажется, помню, что у StringBuilder и MemoryStream разные требования.