Шаблон проектирования для блока try/catch для OutOfMemoryException в .NET

У меня есть приложение, которое работает с большими объемами данных, и я думаю, что, может быть, иногда будет выбрасываться OutOfMemoryException (За пол года у меня не было ни одного исключения, но я просто хочу знать все о Это). Как я выяснил, после этого исключения я не могу продолжить выполнение своей программы.

Есть ли хороший шаблон для обработки таких исключений, особенно для работы с классами IDisposable?


person VMAtm    schedule 09.07.2011    source источник
comment
Разработка приложения для обработки OutOfMemoryException — это, вероятно, последнее, что вам следует делать. Не то, чтобы не делать этого, но есть другие вещи, которые вы должны сделать до этого. Память никогда не бывает бесконечным ресурсом, и хорошо спроектированное приложение всегда должно работать до тех пор, пока выполняются некоторые минимальные критерии, но если ваше приложение может использовать много памяти для ускорения работы, оно должно быть спроектировано именно таким образом. вместо того, чтобы быть спроектированным так, как требует много памяти. Вы уверены, что сделали достаточно, чтобы предотвратить исключение OutOfMemoryException?   -  person Lasse V. Karlsen    schedule 09.07.2011
comment
@ Лассе В. Карлсен У меня никогда не было такого исключения в моем проекте - мне просто любопытно, есть ли какие-нибудь подсказки, которые я должен знать для этого.   -  person VMAtm    schedule 09.07.2011
comment
@VMAtm, запустите инструмент профилирования памяти, чтобы убедиться, что у вас нет утечки памяти. Это первоочередная задача.   -  person myermian    schedule 09.07.2011
comment
@myermian У меня нет с этим проблем - мне просто было интересно узнать об этом.   -  person VMAtm    schedule 09.07.2011
comment
@VMAtm: есть более полезные вещи, о которых стоит поинтересоваться - то, что действительно происходит, а не то, чего еще не произошло.   -  person John Saunders    schedule 09.07.2011
comment
Есть причины для понижения?   -  person VMAtm    schedule 11.11.2015


Ответы (3)


В подлинном сценарии OOM (скорее на x86, чем на x64) вы обречены. Почти что угодно может вызвать выделение, поэтому ваш лучший вариант — умереть как можно быстрее и элегантнее, причинив минимум вреда.

Поскольку этого не происходит, не напрягайтесь слишком сильно, но лучше предотвратить, чем действовать здесь:

  • использовать API потоковых данных, а не буферизовать все в памяти
  • повторное использование буферов и т. д.
  • избегайте огромных массивов/списков/и т. д. (по правде говоря, наиболее вероятным способом вызвать OOM является запрос огромного (но единственного) массива) — например, зубчатый массив масштабируется лучше, чем 2D-массив (даже на x64 есть жесткое ограничение на максимальный размер одного массива)
  • подумайте о том, как вы обрабатываете разреженные данные
  • Вы читаете много строк из внешних источников? Если это так, рассмотрите возможность использования собственного внутреннего модуля, чтобы у вас не было 20 000 различных копий общих строк (например, названий стран).
  • следите за тем, что вы выпускаете, когда
  • избегайте случайного продления жизни объектов, особенно с помощью подписки на события (известной случайным продлением жизни)
person Marc Gravell    schedule 09.07.2011
comment
У меня очень мало знаний по этой теме, но ваш 1-й абзац, кажется, противоречит ответам на Является ли «Недостаточно памяти» исправимой ошибкой?. Если я что-то пропустил, не могли бы вы пояснить, пожалуйста? - person Alexander Malakhov; 25.11.2011

Нет хорошего шаблона, OOM — неприятное исключение, которое может ударить в любой момент. Что делает его почти асинхронным исключением. Единственные шансы справиться с этим у вас есть, когда ваш код структурирован для одновременного выделения больших объемов памяти. Таким образом, у вас будут некоторые шансы отступить и раскрутить состояние программы, как будто ничего не произошло.

Вы должны спроектировать свою программу таким образом, чтобы ей никогда не приходилось выделять более половины всей доступной виртуальной памяти, один гигабайт на 32-разрядной машине. Драконы живут сверх этого объема, ваша программа не сможет выделить 90 МБ или меньше, даже если есть еще 500 МБ неиспользуемой виртуальной памяти. Проблема, вызванная фрагментацией адресного пространства. Если вы регулярно пересекаете этот порог, вам необходимо переключиться на 64-разрядную операционную систему.

person Hans Passant    schedule 09.07.2011
comment
Я столкнулся с ошибками нехватки памяти, когда было выделено намного меньше гигабайта. Я подозреваю, что объект COM Dictionary может выделять мелкие вещи в куче больших объектов, что приводит к массивной неприятной фрагментации. Я озадачен обоснованием обращения Microsoft с LOH, но это именно то, что есть. - person supercat; 11.07.2011
comment
Фрагментация адресного пространства всегда портит вам настроение. Вы никогда не можете выделять более ~550 МБ сразу после запуска программы. Это идет очень быстро вниз по склону оттуда. COM не выделяет из LOH, он использует свою собственную кучу. - person Hans Passant; 11.07.2011
comment
Я ожидал этого, но по какой-то причине запуск COM-DLL из моего приложения привел к тому, что память вышла из-под контроля, когда я переключил эту DLL на использование словаря, а не коллекции; когда DLL запускается как отдельный процесс, совместное использование памяти диспетчером задач DLL + приложение в конечном итоге составляет около трети использования приложения, когда оба выполняются вместе. - person supercat; 11.07.2011

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

На своей предыдущей работе я использовал такой инструмент (HP's Performance Center), и он оказался бесценным не только для проверки ошибок и ограничений нашей системы, но и для выявления узких мест и дорогостоящих операций.

person J. Ed    schedule 09.07.2011