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

С#: исключение из памяти

Сегодня мое приложение сегодня выбрало OutOfMemoryException. Для меня это всегда было почти невозможно, так как у меня 4 ГБ оперативной памяти и много виртуальной памяти тоже. Ошибка произошла, когда я попытался добавить существующую коллекцию в новый список.

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);  

Насколько я понимаю, здесь не так много памяти, поскольку транспортные средства, которые мой новый список должен содержать, уже существуют внутри памяти. Я должен признать, что Vehicle - очень сложный класс, и я попытался добавить около 50 000 элементов в новый список за один раз. Но так как все Vehicle в приложении поступают из базы данных размером 200 МБ: я не знаю, что может вызвать OutOfMemoryException на этом этапе.

4b9b3361

Ответ 1

Две точки:

  • Если вы используете 32-битную Windows, у вас не будет всего доступного 4 ГБ, всего 2 ГБ.
  • Не забывайте, что базовая реализация List - это массив. Если ваша память сильно фрагментирована, может быть недостаточно смежного пространства для выделения вашего List, хотя в общей сложности у вас много свободной памяти.

Ответ 2

3 года, но я нашел другое рабочее решение. Если вы уверены, что у вас достаточно свободной памяти, работающей на 64-битной ОС и все еще получающей исключения, обязательно установите этот параметр в свойствах проекта enter image description here

Ответ 3

.Net4.5 больше не имеет ограничения на 2 ГБ для объектов. Добавьте эти строки в App.config

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

и можно будет создавать очень большие объекты, не получив OutOfMemoryException

Обратите внимание, что он будет работать только на ОС x64!

Ответ 4

Данные, хранящиеся в базе данных по сравнению с памятью в приложении, сильно отличаются.

Невозможно получить точный размер вашего объекта, но вы можете сделать это:

GC.GetTotalMemory() 

После того, как загружено определенное количество объектов, и посмотрите, сколько меняется ваша память при загрузке списка.

Если это список, который вызывает чрезмерное использование памяти, мы можем посмотреть способы его минимизации. Например, почему вы хотите, чтобы 50 000 объектов загружались в память сразу. Не лучше ли было бы вызывать БД по мере необходимости?

Если вы посмотрите здесь: http://www.dotnetperls.com/array-memory, вы также увидите, что объекты в .NET больше, чем их фактические данные. Общий список - это еще больший объем памяти, чем массив. Если у вас есть общий список внутри вашего объекта, он будет расти еще быстрее.

Ответ 5

OutOfMemoryException (на 32-разрядных машинах) так же часто встречается в Fragmentation как реальные жесткие ограничения на память - вы найдете много об этом, но здесь мой первый хит Google кратко обсуждает его: http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx. (@Anthony Pegram ссылается на ту же проблему в своем комментарии выше).

Тем не менее, есть еще одна возможность, которая приходит в голову для вашего кода выше: поскольку вы используете конструктор "IEnumerable" в List, вы можете не давать объекту никаких подсказок, как к размеру коллекции, которую вы передаете в конструктор списка. Если объект, который вы передаете, не является коллекцией (не реализует интерфейс ICollection), то за кадром будет реализована реализация в List (несколько) раз, каждый раз оставляя за собой слишком - Малый массив, который нужно собрать мусором. Вероятно, сборщик мусора не дойдет до этих отброшенных массивов, и вы получите свою ошибку.

Простейшим решением для этого было бы использовать конструктор List(int capacity), чтобы сообщить инфраструктуре, какой размер массива поддержки можно выделить (даже если вы оцениваете и просто гадаете "50000", например), а затем используйте AddRange(IEnumerable collection) для фактического заполнения списка.

Итак, простейшее "Fix", если я прав: замените

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);

с

List<Vehicle> vList = new List<Vehicle>(50000);  
vList.AddRange(selectedVehicles);

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

Примечание (как @Alex прокомментировал ниже), это только проблема, если selectedVehicles не является ICollection.

Ответ 6

Моя команда разработчиков решила эту ситуацию:

Мы добавили следующий пост-Build script в проект .exe и скомпилировали снова, установив цель на x86 и увеличившись на 1,5 Гб, а также x64 Платформа увеличила объем памяти с использованием 3,2 ГБ. Наше приложение 32 бит.

Связанные URL-адреса:

Script:

if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
    call "$(DevEnvDir)..\tools\vsvars32.bat"
    editbin /largeaddressaware "$(TargetPath)"
)

Ответ 7

Вы не должны пытаться привести весь список сразу, т.е. размер элементов в базе данных не совпадает с размером, который он занимает в памяти. Если вы хотите обработать элементы, вы должны использовать a для каждого цикла и использовать ленивую загрузку фреймворка сущности, чтобы вы не сразу вносили все элементы в память. Если вы хотите показать список, используйте разбиение на страницы (.Skip() и .take())