С#: исключение из памяти
Сегодня мое приложение сегодня выбрало OutOfMemoryException
. Для меня это всегда было почти невозможно, так как у меня 4 ГБ оперативной памяти и много виртуальной памяти тоже. Ошибка произошла, когда я попытался добавить существующую коллекцию в новый список.
List<Vehicle> vList = new List<Vehicle>(selectedVehicles);
Насколько я понимаю, здесь не так много памяти, поскольку транспортные средства, которые мой новый список должен содержать, уже существуют внутри памяти. Я должен признать, что Vehicle
- очень сложный класс, и я попытался добавить около 50 000 элементов в новый список за один раз. Но так как все Vehicle
в приложении поступают из базы данных размером 200 МБ: я не знаю, что может вызвать OutOfMemoryException
на этом этапе.
Ответы
Ответ 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())