Подтвердить что ты не робот

"System.OutOfMemoryException" было брошено, когда еще много свободного места

Это мой код:

int size = 100000000;
double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb
double[] randomNumbers = new double[size];

Исключение: Исключено исключение типа "System.OutOfMemoryException".

У меня 4 ГБ памяти на этой машине 2.5 ГБ бесплатно, когда я запускаю этот запуск, на ПК достаточно места для обработки 762 МБ из 100000000 случайных чисел. Мне нужно хранить как можно больше случайных чисел с учетом доступной памяти. Когда я пойду на производство, на коробке будет 12 ГБ, и я хочу использовать его.

Скрывает ли среда CLR максимальную память по умолчанию? и как я могу запросить больше?

Обновление

Я думал, что это взломать на более мелкие куски, и поэтапное добавление к моим требованиям к памяти поможет, если проблема связана с фрагментацией памяти, но она не , я не могу пройти мимо общий размер массива ArrayList размером 256 МБ, независимо от того, что я делаю для создания блока blockSize.

private static IRandomGenerator rnd = new MersenneTwister();
private static IDistribution dist = new DiscreteNormalDistribution(1048576);
private static List<double> ndRandomNumbers = new List<double>();

private static void AddNDRandomNumbers(int numberOfRandomNumbers) {
    for (int i = 0; i < numberOfRandomNumbers; i++) {
      ndRandomNumbers.Add(dist.ICDF(rnd.nextUniform()));                
  }
}

Из моего основного метода:

int blockSize = 1000000;

while (true) {
  try
  {
    AddNDRandomNumbers(blockSize);                    
  }
  catch (System.OutOfMemoryException ex)
  {
    break;
  }
}            
double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0;
4b9b3361

Ответ 1

Вы можете прочитать следующее: "" Из памяти "не относится к физической памяти" Эрика Липперта.

Короче говоря, очень упрощенно, "Out of memory" на самом деле не означает, что объем доступной памяти слишком мал. Наиболее распространенная причина заключается в том, что в текущем адресном пространстве нет смежной части памяти, которая достаточно велика, чтобы служить желаемому распределению. Если у вас есть 100 блоков, каждый размером 4 МБ, который не поможет вам, когда вам нужен один блок размером 5 МБ.

Ключевые моменты:

  • хранилище данных, которое мы называем "память процесса", на мой взгляд лучше всего визуализируется как массив на диске.
  • ОЗУ можно рассматривать как просто оптимизацию производительности.
  • Общий объем виртуальной памяти, потребляемой вашей программой, действительно не имеет особого значения для ее производительности.
  • "нехватка ОЗУ" редко приводит к ошибке "из памяти". Вместо ошибки это приводит к плохой производительности, потому что полная стоимость того факта, что хранилище фактически на диске внезапно становится актуальной.

Ответ 2

У вас нет непрерывного блока памяти, чтобы выделить 762 МБ, ваша память фрагментирована, и распределитель не может найти достаточно большое отверстие для выделения необходимой памяти.

  • Вы можете попытаться работать с /3GB (как предложили другие)
  • Или переключиться на 64-разрядную ОС.
  • Или измените алгоритм, чтобы он не нуждался в большой части памяти. возможно, выделите несколько меньших (относительно) блоков памяти.

Ответ 3

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

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

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

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

Ответ 4

Как вы, вероятно, выяснили, проблема в том, что вы пытаетесь выделить один большой непрерывный блок памяти, который не работает из-за фрагментации памяти. Если бы мне нужно было делать то, что вы делаете, я бы сделал следующее:

int sizeA = 10000,
    sizeB = 10000;
double sizeInMegabytes = (sizeA * sizeB * 8.0) / 1024.0 / 1024.0; //762 mb
double[][] randomNumbers = new double[sizeA][];
for (int i = 0; i < randomNumbers.Length; i++)
{
    randomNumbers[i] = new double[sizeB];
}

Затем, чтобы получить конкретный индекс, вы должны использовать randomNumbers[i / sizeB][i % sizeB].

Другой вариант, если вы всегда обращаетесь к значениям в порядке, может заключаться в использовании перегруженного конструктора для указания семени. Таким образом, вы получите полуслучайное число (например, DateTime.Now.Ticks) сохраните его в переменной, а затем, когда вы начнете проходить через вы должны создать новый экземпляр Random с использованием исходного семени:

private static int randSeed = (int)DateTime.Now.Ticks;  //Must stay the same unless you want to get different random numbers.
private static Random GetNewRandomIterator()
{
    return new Random(randSeed);
}

Важно отметить, что, хотя блог, связанный с ответом Fredrik Mörk, указывает, что проблема связана с отсутствием адресного пространства, он не перечисляет ряд других проблем, таких как ограничение размера объекта CLR на 2 ГБ (упомянутое в комментарий от ShuggyCoUk в том же блоге), замалчивает фрагментацию памяти и не упоминает о влиянии размера файла страницы (и как его можно решить с помощью CreateFileMapping).

Ограничение 2GB означает, что randomNumbers должно быть меньше 2 ГБ. Поскольку массивы являются классами и имеют некоторые накладные расходы, это означает, что массив double должен быть меньше, чем 2 ^ 31. Я не уверен, насколько меньше, чем 2 ^ 31 Длина должна была бы быть, но Накладные расходы .NET-массива? указывает 12 - 16 байт.

Фрагментация памяти очень похожа на фрагментацию жесткого диска. У вас может быть 2 ГБ адресного пространства, но при создании и уничтожении объектов между значениями будут пробелы. Если эти пробелы слишком малы для вашего большого объекта, и дополнительное пространство не может быть запрошено, вы получите System.OutOfMemoryException. Например, если вы создаете 2 миллиона 1024-байтных объектов, то вы используете 1,9 ГБ. Если вы удаляете каждый объект, где адрес не кратен 3, тогда вы будете использовать .6 ГБ памяти, но он будет распространяться через адресное пространство с 2024 байтами открытых блоков между ними. Если вам нужно создать объект, который был .2GB, вы бы не смогли этого сделать, потому что не было достаточно большого блока, чтобы вместить его, и дополнительное пространство не может быть получено (при условии 32-разрядной среды). Возможные решения этой проблемы - это такие вещи, как использование небольших объектов, уменьшение объема данных, хранящихся в памяти, или использование алгоритма управления памятью для ограничения/предотвращения фрагментации памяти. Следует отметить, что, если вы не разрабатываете большую программу, которая использует большой объем памяти, это не будет проблемой. Кроме того, эта проблема может возникнуть в 64-битных системах, поскольку окна ограничены главным образом размером файла страницы и объемом оперативной памяти в системе.

Поскольку большинство программ запрашивают рабочую память из ОС и не запрашивают сопоставление файлов, они будут ограничены системным ОЗУ и размером файла страницы. Как отмечается в комментарии Нестора Санчеса (Néstor Sánchez) в блоге, с управляемым кодом, подобным С#, вы застреваете в ограничении RAM/файла страницы и адресном пространстве операционной системы.


Это было намного дольше, чем ожидалось. Надеюсь, это помогает кому-то. Я опубликовал его, потому что я столкнулся с System.OutOfMemoryException запуском x64-программы в системе с 24 ГБ оперативной памяти, хотя в моем массиве было всего 2 ГБ.

Ответ 5

Я бы посоветовал использовать опцию загрузки Windows/3GB. Помимо всего прочего (это слишком сложно сделать для одного плохого приложения, и это, вероятно, не решит вашу проблему), это может вызвать большую нестабильность.

Многие драйверы Windows не тестируются с этой опцией, поэтому многие из них предполагают, что указатели пользовательского режима всегда указывают на более низкое 2 ГБ адресного пространства. Это означает, что они могут сильно разрушиться /3GB.

Однако Windows обычно ограничивает 32-разрядный процесс адресным пространством 2 ГБ. Но это не значит, что вы должны ожидать, что сможете выделить 2 ГБ!

В адресное пространство уже завалены всевозможные распределенные данные. Там находятся стек и все загружаемые сборки, статические переменные и т.д. Там нет гарантии, что в любом месте будет нераспределенная память в 800 МБ.

Выделение 2 400 Мбайт кусков, вероятно, будет лучше. Или 4 200 Мб кусков. Меньшие распределения намного легче найти место для фрагментированного пространства памяти.

В любом случае, если вы все равно собираетесь развернуть это на машине на 12 ГБ, вы захотите запустить это как 64-битное приложение, которое должно решить все проблемы.

Ответ 6

С менялось от 32 до 64 бит - стоит попробовать, если вы на 64-битном ПК, и его не нужно переносить.

Ответ 8

32-битные окна имеют ограничение памяти процесса 2 ГБ. Опция загрузки /3GB, о которой упоминалось выше, сделает это 3 ГБ с остатком 1 ГБ для использования ядра ОС. Реально, если вы хотите использовать более 2 ГБ без хлопот, требуется 64-битная ОС. Это также преодолевает проблему, при которой, хотя у вас может быть 4 ГБ физической памяти, адресное пространство, требуемое для видеокарты, может сделать размерный патрон этой памяти непригодным для использования - обычно около 500 МБ.

Ответ 9

Вместо выделения массивного массива вы можете попробовать использовать итератор? Они выполняются с задержкой, то есть значения генерируются только по мере их запроса в инструкции foreach; вы не должны исчерпывать память следующим образом:

private static IEnumerable<double> MakeRandomNumbers(int numberOfRandomNumbers) 
{
    for (int i = 0; i < numberOfRandomNumbers; i++)
    {
        yield return randomGenerator.GetAnotherRandomNumber();
    }
}


...

// Hooray, we won't run out of memory!
foreach(var number in MakeRandomNumbers(int.MaxValue))
{
    Console.WriteLine(number);
}

Вышеуказанное будет генерировать столько случайных чисел, сколько вы хотите, но только генерировать их, как их просят с помощью инструкции foreach. Таким образом, у вас не будет нехватки памяти.

В качестве альтернативы, если вы должны иметь их всех в одном месте, сохраните их в файле, а не в памяти.

Ответ 10

Ну, у меня возникла аналогичная проблема с большим набором данных и попытка заставить приложение использовать так много данных, это не совсем правильный вариант. Лучший совет, который я могу вам дать, - это обработать ваши данные в небольшом фрагменте, если это возможно. Поскольку дело касается стольких данных, проблема рано или поздно вернется. Кроме того, вы не можете знать конфигурацию каждой машины, которая будет запускать ваше приложение, поэтому всегда существует риск того, что исключение произойдет на другом ПК.

Ответ 11

У меня была аналогичная проблема, это было связано с StringBuilder.ToString();

Ответ 12

Преобразуйте решение в x64. Если вы все еще сталкиваетесь с проблемой, укажите максимальную длину для всего, что генерирует исключение, подобное ниже:

 var jsSerializer = new JavaScriptSerializer();
 jsSerializer.MaxJsonLength = Int32.MaxValue;

Ответ 13

Увеличьте предел процесса Windows до 3gb. (через boot.ini или менеджер загрузки Vista)