2011-12-19 3 views
51

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

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

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

+7

Что такое значение (и тип) 'selectedVehicles'? – harold

+1

Когда было выбрано 'OutOfMemoryException', вы подключились к процессу с помощью отладчика и посмотрели, в чем проблема? Насколько велики были * объекты? .NET Framework имеет жесткий предел в 2 ГБ для размера объекта, минус накладные расходы, потребляемые самой картой. –

+1

Возможно ли транспортное средство, а не класс? –

ответ

57

Две точки:

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

У вас есть только 2 ГБ в 64-битной Windows. Это ограничение .NET Framework, а не только 32-разрядного адресного пространства. –

+0

+1 Для пункта 2. Он может попытаться написать фрагментированный список. –

+22

@CodyGray Это будет 2 ГБ за объект (массив), а не всего 2 ГБ. –

10

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

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

GC.GetTotalMemory() 

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

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

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

7

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

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

Простейшим решением для этого было бы использовать конструктор 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.

+3

Если selecyedVehicles - это коллекция, конструктор будет выделять правильный размер массива. Нет необходимости проходить через AddRange. – Alex

3

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

58

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

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

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

Пожалуйста, обратите внимание, что будет работать только на 64-разрядных ОС!

+0

Красиво сделано. Это сработало для меня, отметив, что нужно изменить цель сборки на x64. –

+0

также относится к ASP.NET 4.5? Используя локальный отчет, * My App v1 (asp.net 3.5 - clr 2.0 - classic) * работает *** OK ***, но мое приложение v2 (asp.net 4.5, clr 4.0, classic) генерирует *** OutOfMemoryException error ***, в том же сервере IIS – Kiquenet

67

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

+1

нет изображения – TheGameiswar

4

Мои развития решить эту ситуацию:

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

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

Сценарий:

if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
    call "$(DevEnvDir)..\tools\vsvars32.bat" 
    editbin /largeaddressaware "$(TargetPath)" 
) 
Смежные вопросы