- Система.OutOfMemoryException " был выброшен, когда еще много свободной памяти

Это мой код:

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

исключение: Исключение типа 'System.OutOfMemoryException' был брошен.

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

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

обновление

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

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;

13 ответов


вы можете прочитать это:" "из памяти" не относится к физической памяти" Эрика Липперта.

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

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

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

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

  1. вы можете попробовать работать с /3GB (как предлагали другие)
  2. или переключитесь на 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 ГБ. Надеюсь, если вы все еще хотите создать массивы больше 2GB, вы можете сделать это, добавив следующий код для вас приложение.конфигурационный файл:

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

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

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) сохраните его в переменной, затем, когда вы начнете просматривать список, вы создадите новый случайный экземпляр, используя исходное семя:

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

важно отметить, что в то время как блог, связанный в ответе Фредрика Мерка, указывает, что проблема обычно связана с отсутствием адресного пространства он не перечисляет ряд других проблем, таких как ограничение размера объекта CLR 2GB (упомянутое в комментарий от 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-битных системах, поскольку windows ограничена в основном размером файла страницы и объемом ОЗУ в системе.

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


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


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

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

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

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

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

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


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


Если вам нужны такие большие структуры, возможно, вы могли бы использовать сопоставленные с памятью файлы. Эта статья может оказаться полезной: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

LP, Дежан!--3-->


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


вместо выделения массивного массива, не могли бы вы попробовать использовать итератор? Они выполняются с задержкой, то есть значения генерируются только так, как они запрашиваются в инструкции 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. Так у тебя память не кончится.

С другой стороны, если вы должны иметь их все в одном месте, хранить их в файле, а не в памяти.


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


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


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

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

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