Это мой код:
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;
источник
Ответы:
Вы можете прочитать это: « Недостаточно памяти» не относится к физической памяти »Эрика Липперта.
Короче говоря, и очень упрощенно, «Недостаточно памяти» на самом деле не означает, что объем доступной памяти слишком мал. Наиболее частая причина заключается в том, что в текущем адресном пространстве нет непрерывной части памяти, достаточно большой для обслуживания желаемого распределения. Если у вас 100 блоков, каждый размером 4 МБ, это не поможет вам, когда вам понадобится один блок размером 5 МБ.
Ключевые моменты:
источник
Убедитесь, что вы создаете 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>
источник
У вас нет непрерывного блока памяти для выделения 762 МБ, ваша память фрагментирована, и распределитель не может найти достаточно большое отверстие для выделения необходимой памяти.
источник
Как вы, наверное, догадались, проблема в том, что вы пытаетесь выделить один большой непрерывный блок памяти, который не работает из-за фрагментации памяти. Если бы мне нужно было сделать то, что делаете вы, я бы сделал следующее:
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 2 ГБ (упомянуто в комментарии от ShuggyCoUk в том же блоге), приукрашивает фрагментацию памяти и не упоминает влияние размера файла подкачки (и то, как это можно решить с помощью
CreateFileMapping
функции ).Ограничение 2 ГБ означает, что он
randomNumbers
должен быть менее 2 ГБ. Поскольку массивы являются классами и сами по себе имеют некоторые накладные расходы, это означает, что массивdouble
должен быть меньше 2 ^ 31. Я не уверен, насколько должна быть длина меньше 2 ^ 31, но накладные расходы на массив .NET? обозначает 12-16 байтов.Фрагментация памяти очень похожа на фрагментацию жесткого диска. У вас может быть 2 ГБ адресного пространства, но по мере создания и уничтожения объектов между значениями будут промежутки. Если эти зазоры слишком малы для вашего большого объекта, и дополнительное пространство не может быть запрошено, тогда вы получите
System.OutOfMemoryException
. Например, если вы создаете 2 миллиона объектов по 1024 байта, вы используете 1,9 ГБ. Если вы удалите каждый объект, адрес которого не кратен 3, вы будете использовать 0,6 ГБ памяти, но он будет распределен по адресному пространству с 2024-байтовыми открытыми блоками между ними. Если вам нужно создать объект размером 0,2 ГБ, вы не сможете это сделать, потому что нет блока, достаточно большого для его размещения, и невозможно получить дополнительное пространство (при условии 32-битной среды). Возможные решения этой проблемы - это использование более мелких объектов, уменьшение объема данных, которые вы храните в памяти, или использование алгоритма управления памятью для ограничения / предотвращения фрагментации памяти. Следует отметить, что если вы не разрабатываете большую программу, которая использует большой объем памяти, это не будет проблемой. Также,Поскольку большинство программ запрашивают рабочую память у ОС и не запрашивают сопоставление файлов, они будут ограничены оперативной памятью системы и размером файла подкачки. Как отмечено в комментарии Нестора Санчеса (Néstor Sánchez) в блоге, с управляемым кодом, таким как C #, вы привязаны к ограничению RAM / файла страницы и адресному пространству операционной системы.
Это было намного дольше, чем ожидалось. Надеюсь, это кому-то поможет. Я разместил его, потому что я столкнулся с
System.OutOfMemoryException
запуском программы x64 в системе с 24 ГБ ОЗУ, хотя в моем массиве было всего 2 ГБ.источник
Я бы не советовал использовать вариант загрузки Windows / 3GB. Помимо всего прочего (делать это для одного плохо работающего приложения, и это, вероятно, в любом случае не решит вашу проблему), это может вызвать большую нестабильность.
Многие драйверы Windows не тестируются с этой опцией, поэтому многие из них предполагают, что указатели пользовательского режима всегда указывают на нижние 2 ГБ адресного пространства. Это означает, что они могут ужасно сломаться с / 3GB.
Однако Windows обычно ограничивает 32-разрядный процесс адресным пространством 2 ГБ. Но это не значит, что вы должны рассчитывать на возможность выделить 2 ГБ!
Адресное пространство уже завалено всякими выделенными данными. Есть стек и все загруженные сборки, статические переменные и так далее. Нет никакой гарантии, что где-либо будет 800 МБ непрерывной нераспределенной памяти.
Выделение фрагментов размером 2 400 МБ, вероятно, будет лучше. Или 4 фрагмента по 200 МБ. Во фрагментированном пространстве памяти гораздо легче найти место для меньших распределений.
В любом случае, если вы все равно собираетесь развернуть это на машине 12 ГБ, вам нужно запустить это как 64-разрядное приложение, которое должно решить все проблемы.
источник
У меня сработал переход с 32 на 64 бит - стоит попробовать, если у вас 64-битный компьютер и его не нужно переносить.
источник
Если вам нужны такие большие структуры, возможно, вы могли бы использовать файлы с отображением памяти. Эта статья может оказаться полезной: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx
LP, Деян
источник
32-битные окна имеют ограничение памяти процесса 2 ГБ. Вариант загрузки / 3GB, о котором упоминали другие, сделает эти 3 ГБ, а для использования ядра ОС останется всего 1 ГБ. На самом деле, если вы хотите без проблем использовать более 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;
источник
Если вам не нужен процесс размещения Visual Studio:
Снимите флажок с опции: Project-> Properties-> Debug-> Enable the Visual Studio Hosting Process.
А потом строить.
Если проблема не исчезла:
Перейдите в Project-> Properties-> Build Events-> Командная строка события после сборки и вставьте следующее:
call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86 "$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)" /LARGEADDRESSAWARE
Теперь создайте проект.
источник
Увеличьте ограничение процесса Windows до 3 ГБ. (через boot.ini или диспетчер загрузки Vista)
источник