System outofmemoryexception недостаточно памяти

Обновлено: 29.06.2024

мои плюсы в обращении с ним были:

  • тот факт, что OutOfMemoryException был брошен, как правило, не означает, что состояние программы было повреждено;
  • согласно документации " следующие инструкции Microsoft intermediate (MSIL) выбрасывают исключение OutOfMemoryException: box, newarr, newobj" что просто (обычно) означает, что среда CLR попыталась найти блок памяти заданного размера и не смогла этого сделать; она делает не означает, что в нашем распоряжении не осталось ни одного байта;

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

поэтому мой вопрос: каковы серьезные причины не обрабатывать OutOfMemoryException и сразу же отказаться, когда это происходит?

редактировать: считаете ли вы, что OOME так же фатально, как ExecutionEngineException?

мы все пишем разные приложения. В WinForms или ASP.Net app я бы, вероятно, просто войти исключение, уведомить Пользователя, попытаться сохранить состояние и выключения / перезагрузки. Но, как отметил Игорь в комментариях, это вполне может быть от создания какой-то формы приложения для редактирования изображений, и процесс загрузки 100-го 20MB RAW-изображения может подтолкнуть приложение к краю. Вы действительно хотите использовать, чтобы потерять всю свою работу от чего-то простого, как сказать. "Извините, не удалось загрузить больше изображения в это время".

Не забывайте, что мы все разрабатываем различные типы приложений. И если вы не находитесь на старых, ограниченных машинах, вы, вероятно, никогда не получите исключение OutOfMemoryException для типичных бизнес-приложений. но опять же не все из нас являются бизнес-инструментом разработчики.

для редактирования.

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

IMO, так как вы не можете предсказания что вы можете / не можете сделать после OOM (так что вы не можете надежно обработать ошибку), или что другое did/didn't произошло при развертывании стека туда, где вы находитесь (поэтому BCL не надежно обработал ошибку), ваше приложение теперь должно быть предположить быть в коррумпированном состоянии. Если вы" исправите " свой код, обработав это исключение, вы зарываетесь головой в песок.

Если вам нужно шанс обработка некоторых данных и рады, что это не удастся: запустите второй процесс ( AppDomain недостаточно хорош). Если он взорвется, снесите процесс. Проблема решена, и ваш оригинальный процесс/ AppDomain безопасно.

некоторые комментаторы отметили, что бывают ситуации, когда OOM может быть непосредственным результатом попытки выделить большое количество байтов (графическое приложение, выделение большого массива и т. д.). Обратите внимание, что для этой цели вы можете использовать MemoryFailPoint класс, который выдает InsufficientMemoryException (само производное от OutOfMemoryException). Это can быть пойманным безопасно, так как он поднят до фактическая попытка для выделения памяти было сделано. Однако, это может только реально сократить likelyness в ООМ, не полностью предотвратить его.

все зависит от ситуации.

У нас все еще была проблема, если геометрия была > 2GB, но это была другая история.

очевидно, если вы получаете ошибку из памяти с чем - то фундаментальным для вашего приложения, то у вас нет выбора, кроме как закрыть-но сделать это так изящно, как вы можете.

предлагаю комментировать Кристофер Brumme в "рамках правил проектирования", стр. 238 (7.3.7 OutOfMemoryException):

на одном конце спектра исключение OutOfMemoryException может быть результатом неспособности получить 12 байтов для неявного автобоксинга или неспособности JIT некоторый код, необходимый для критического возврата. Эти случаи являются катастрофическими сбоями и в идеале приведут к прекращению процесса. На другом конце спектра OutOfMemoryException может в следствии нить просить 1 байт ГБ массива. Тот факт, что мы провалили эту попытку выделения средств, не влияет на последовательность и жизнеспособность остального процесса.

печальный факт заключается в том, что CRL 2.0 не может различать какие-либо точки в этом спектре. В большинстве управляемых процессов все OutOfMemoryExceptions считаются эквивалентными, и все они приводят к распространению управляемого исключения в потоке. Однако, вы не можете зависеть от вашего кода возврата выполняется, потому что мы можем не выполнить некоторые из ваших методов backout, или мы можем не выполнить статические конструкторы, необходимые для backout.

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

Марк Gravell уже дал отличный ответ; видя, как я частично "вдохновил" на этот вопрос, я хотел бы добавить одну вещь:

одним из основных принципов обработки исключений является никогда не выбрасывать исключение внутри обработчика исключений. (Примечание-повторное выбрасывание доменного и / или обернутого исключения в порядке; я говорю о неожиданный исключение.)

есть всевозможные причины, по которым вам нужно предотвратить это из происходящего:

в лучшем случае вы маскируете исходное исключение; становится невозможным точно знать, где программа изначально не удалась.

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

так какое это имеет отношение к OutOfMemoryException конкретно?

An OutOfMemoryException ничего не говорит вам о почему ошибка выделения памяти. Вы могли бы!--9-->предположим это было потому, что вы пытались выделить огромный буфер, но, возможно, это было не так. Возможно, какой-то другой процесс rogue в системе буквально потребил все доступное адресное пространство, и у вас нет остался один байт. Может быть, какая-то другая нить в собственные программы пошло наперекосяк и вошло в бесконечный цикл, выделяя новую память на каждой итерации, и этот поток уже давно не удался к моменту OutOfMemoryException заканчивается на вашем текущем кадре стека. Дело в том, что вы на самом деле не знаю, насколько плохая ситуация с памятью, даже если вы думаете, что делаете.

Итак, мой простой ответ на этот вопрос: никогда.

мой ласка-ответ на этот вопрос:это нормально в глобальном обработчике исключений, если вы действительно очень осторожны. Не в блоке try-catch.

Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.

Исключение, которое выбрасывается при недостаточном объеме памяти для выполнения программы.

Комментарии

OutOfMemoryException использует HRESULT со COR_E_OUTOFMEMORY значением 0x8007000E.

Список начальных значений свойств для экземпляра OutOfMemoryException, см. в разделе OutOfMemoryException конструкторы.

Значение унаследованного Data свойства всегда равно null .

OutOfMemoryExceptionИсключение имеет две основные причины.

Предпринимается попытка расширения StringBuilder объекта, длина которого превышает длину, определенную его StringBuilder.MaxCapacity свойством.

Среда CLR не может выделить достаточно непрерывной памяти для успешного выполнения операции. Это исключение может быть создано любым назначением свойства или вызовом метода, требующего выделения памяти. Дополнительные сведения о причине OutOfMemoryException исключения см. в записи блога "недостаточно памяти" не относится к физической памяти.

Этот тип OutOfMemoryException исключения представляет разрушительный сбой. Если решено решить эту ошибку, следует включить catch блок, вызывающий Environment.FailFast метод, чтобы завершить работу приложения и добавить запись в журнал системных событий, как показано в следующем примере.

Некоторые условия, при которых создается исключение, и действия, которые можно предпринять для исключения, включают следующее:

Вызывается StringBuilder.Insert метод.

Предпринимается попытка увеличить длину StringBuilder объекта, превышающего размер, указанный в его StringBuilder.MaxCapacity свойстве. В следующем примере показано исключение, вызываемое OutOfMemoryException вызовом StringBuilder.Insert(Int32, String, Int32) метода, когда в примере предпринимается попытка вставить строку, которая приведет к Length превышению максимального объема свойства объекта.

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

Замените вызов StringBuilder.StringBuilder(Int32, Int32) конструктора на вызов любой другой StringBuilder перегрузки конструктора. Максимальная емкость StringBuilder объекта будет равна значению по умолчанию, то есть Int32.MaxValue .

Вызовите StringBuilder.StringBuilder(Int32, Int32) конструктор со maxCapacity значением, которое достаточно велико для размещения всех расширений StringBuilder объекта.

Приложение выполняется как 32-разрядный процесс.

32-разрядные процессы могут выделять максимум 2 ГБ памяти виртуального режима пользователя на 32-разрядных системах и 4 ГБ виртуальной памяти в режиме пользователя на 64-разрядных системах. Это может усложнить для среды CLR выделение достаточного непрерывного объема памяти, если требуется большое выделение. В отличие от этого, 64-разрядные процессы могут выделить до 8 ТБ виртуальной памяти. Чтобы устранить это исключение, перекомпилируйте приложение, предназначенное для 64-разрядной платформы. сведения о настройке конкретных платформ в Visual Studio см. в разделе как настроить проекты для целевых платформ.

В приложении происходит утечка неуправляемых ресурсов

несмотря на то, что сборщик мусора может освободить память, выделенную для управляемых типов, она не управляет памятью, выделенной для неуправляемых ресурсов, таких как дескрипторы операционной системы (включая дескрипторы файлов, сопоставленных в памяти файлов, каналов, разделов реестра и дескрипторов ожидания) и блоков памяти, выделенных напрямую вызовами API Windows или вызовами функций выделения памяти, таких как malloc . Типы, использующие неуправляемые ресурсы, реализуют IDisposable интерфейс.

Если используется тип, использующий неуправляемые ресурсы, следует обязательно вызвать его IDisposable.Dispose метод после завершения его использования. (Некоторые типы также реализуют Close метод, идентичный функции к Dispose методу.) Дополнительные сведения см. в разделе использование объектов, реализующих интерфейс IDisposable .

Если вы создали тип, использующий неуправляемые ресурсы, убедитесь, что вы реализовали шаблон Dispose и при необходимости указали метод завершения. Дополнительные сведения см. в разделе Реализация метода Dispose и Object.Finalize .

Вы пытаетесь создать большой массив в 64-разрядном процессе

Вы работаете с очень большими наборами данных (например, массивами, коллекциями или наборами данных базы данных) в памяти.

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

Чтобы предотвратить OutOfMemoryException исключения, необходимо изменить приложение таким образом, чтобы меньше данных наделено в памяти, или данные разделены на сегменты, требующие меньшего выделения памяти. Пример:

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

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

Если вы используете большие массивы или другие объекты коллекции, размер которых приводит к OutOfMemoryException исключению, следует изменить приложение для работы с данными в подмножествах, а не для совместной работы с ним.

В следующем примере возвращается массив, состоящий из 200 000 000 значений с плавающей запятой, а затем вычисляется их среднее значение. В выходных данных примера показано, что, поскольку в примере сохраняется весь массив в памяти перед вычислением среднего, OutOfMemoryException создается исключение.

В следующем примере исключение устраняется OutOfMemoryException путем обработки входящих данных без сохранения всего набора данных в памяти, сериализации данных в файл при необходимости для дальнейшей обработки (в данном примере эти строки заносятся в комментарий, так как в данном случае они создают файл, размер которого больше 1 ГБ) и возвращают вычисленное среднее и число вариантов в вызывающую подпрограммы.

Вы постоянно объединяете большие строки.

Так как строки являются неизменяемыми, каждая операция сцепления строк создает новую строку. Влияние на небольшие строки или небольшое количество операций объединения незначительно. Но для больших строк или очень большого числа операций сцепления объединение строк может привести к большому количеству выделений памяти и фрагментации памяти, низкой производительности и, возможно, OutOfMemoryException исключений.

При объединении больших строк или выполнении большого числа операций объединения следует использовать StringBuilder класс вместо String класса. Завершив обработку строки, преобразуйте StringBuilder экземпляр в строку, вызвав StringBuilder.ToString метод.

Закрепление большого количества объектов в памяти.

Оцените, нужно ли закреплять каждый объект,

Убедитесь, что каждый объект отменяется закреплением как можно скорее.

Убедитесь, что каждый вызов GCHandle.Alloc(Object, GCHandleType) метода для крепления памяти имеет соответствующий вызов GCHandle.Free метода для открепления этой памяти.

Следующие инструкции промежуточных инструкций (MSIL) Microsoft вызовут OutOfMemoryException исключение:

Конструкторы

Инициализирует новый экземпляр класса OutOfMemoryException.

Инициализирует новый экземпляр класса OutOfMemoryException с сериализованными данными.

Свойства

Возвращает коллекцию пар «ключ-значение», предоставляющую дополнительные сведения об исключении.

Получает или задает ссылку на файл справки, связанный с этим исключением.

Возвращает или задает HRESULT — кодированное числовое значение, присвоенное определенному исключению.

Возвращает экземпляр класса Exception, который вызвал текущее исключение.

Возвращает или задает имя приложения или объекта, вызывавшего ошибку.

Получает строковое представление непосредственных кадров в стеке вызова.

Возвращает метод, создавший текущее исключение.

Методы

Определяет, равен ли указанный объект текущему объекту.

При переопределении в производном классе возвращает исключение Exception, которое является первопричиной одного или нескольких последующих исключений.

Служит хэш-функцией по умолчанию.

При переопределении в производном классе задает объект SerializationInfo со сведениями об исключении.

Возвращает тип среды выполнения текущего экземпляра.

Создает неполную копию текущего объекта Object.

Создает и возвращает строковое представление текущего исключения.

События

Возникает, когда исключение сериализовано для создания объекта состояния исключения, содержащего сериализованные данные об исключении.

Довольно долго мы боролись за производительность программы WinDraw, а именно за взаимодействие между WinDraw и MS SQL Server.

За время этой борьбы мы сделали несколько серьезных выводов:

2. Железный апгрейд не решает проблему System.OutOfMemoryException, доказано опытным путем. А именно, с момента первого описания проблемы (Проблема производительности WinDraw. ) мы приобрели новый сервер — Hewlett Packard Proliant DL380G6 (2xXeonQC, 32Gb оперативной памяти), на котором установили Microsoft Windows Server 2003 Ent, Microsoft SQL Server 2008 Ent. В настройках Microsoft SQL Server включили опцию Address Windowing Extensions (AWE). Уже на следующий день мы получили ошибку System.OutOfMemoryException, причем судя по Task Manager оперативная память была использована всего НАПОЛОВИНУ.

Исходя из всего этого, и множества советов в интернете(правда большинство советов относилось к работе программы 1С с SQL Server, но проблема была очень похожа на нашу) — решено было попробовать использовать x64 платформу и ПО.

На Этот же самый сервер (Hewlett Packard Proliant DL380G6 (2xXeonQC, 32Gb оперативной памяти)) была установлена операционная система Microsoft Windows Server 2008 R2 Enterprise x64, Microsoft SQL Server 2008 R2 x64, опция Address Windowing Extensions (AWE) выключена (кстати пришла идея попробовать включить и ее. ). Итог потрясающий.

Уже больше месяца мы не получали ошибки System.OutOfMemoryException, хотя оперативная память используется практически полностью!

System.OutOfMemoryException

Исходя из этого данный набор ПО считаем необходимым при одновременном доступе к SQL Server более 30 пользователей.

З.Ы. В ближайшее время попробуем включить опцию Address Windowing Extensions (AWE) и опишем результат!

Немного технической информации!

Механизм Address Windowing Extensions (AWE), используемый в SQL Server, состоит из двух частей, распределяющих физическую память и отображающую её на Virtual Address Space (VAS) данного процесса. Если физическая память распределена, то операционная система уже не сможет её затребовать, пока использующий её процесс не будет завершён или этот процесс освободит память, вернув её операционной системе. Приложение может управлять и даже полностью предотвращать листание. Преимущество механизма mapping/unmapping в том, что одна и та же физическая страница может быть отображена на разные участки VAS. На 64-х битных платформах в unmapping нет необходимости, поскольку VAS мы имеем достаточно, чтобы вместить всю имеющуюся физическую память.

Из теории операционных систем, для описания отображения страницы VAS на физические страницы, система оперирует записями таблицы страниц — Page Table Entry (PTE). Внутри физическая страница описывается номером блока страниц — Page Frame Number (PFN). Из PFN можно получить всю информацию о физической странице, которую он представляет. Например, PFN показывает, какому Non-Uniform Memory Access (NUMA) — узлу принадлежит эта страница. В операционной системе есть база данных, хранящая совокупность PFN, которыми система управляет. Если страница в VAS является закреплённой, существует PTE, который может указывать или не указывать на задействованные PFN. Концептуально, страница, которую представляет PTE, может быть в памяти или нет, например, если она выгружена на диск. В первом случае она привязана к задействованному PFN, а в последнем — нет. В свою очередь, как только физическая страница привязывается к странице в VAS, её PFN возвращаются PTE.

Когда операционная система закрепляет, освобождает, получает/отдаёт страницы задействованного PTE, или должна получить немного информации об этом (например аллокация NUMA), она должно задействовать блокировку рабочего множества процесса — чтобы обеспечить стабильность привязки PTE к PFN. Эта блокировка обходиться довольно дорого и может испортить масштабируемость процесса.

При распределении физических страниц, использование AWE механизма предоставляет нам набор записей PFN непосредственно из базы данных PFN. Операционная система обязана устанавливать блокировку на базу данных PFN во время распределения записей PFN. Используя механизм отображения AWE, Вы можете отобразить аллоцируемые записи PFN на VAS процесса. Когда происходит такое отображение, PTE аллоцируются, привязываются к PFN и отмечаются как блокированые. В этом случае операционная система должна разово установить блокировку рабочего множество процесса. При отображении обычных страниц, операционная система делает это по требованию и, следовательно, должна будет заполучить рабочее множество и установить блокировку в базе данных PFN для каждой страницы. Так как страницы в памяти блокированы, в момент листания эти PTE системой будет игнорироваться.

На 64-х битных платформах лучше называть такие страницы блокированными страницами (locked pages), и, пожалуйста, не путайте их со страницами, блокированными средствами VirtualLock API. Как было описано выше, у блокированных страниц есть два важных свойства — они не участвуют в листании, проводимом операционной системой, и во время распределения они захватывают рабочее множество и устанавливают разовую блокировку в базе данных для PFN.

Первое свойство имеет не очевидное влияние на высокопроизводительные системы, такие, как системы с архитектурой NUMA. Оно приводит к явной резидентности в памяти. Помните, что система закрепляет страницы по требованию. Для распределения физической памяти будет использоваться узел, на котором выполняется обращающийся к памяти поток. Только после этого страница может быть выгружена операционной системой во время свопинга. Далее, она может снова попасть в память, операционная система снова распределит физическую страницу узлу, на котором поток продолжает работать с этой памятью. В этом случае узел может оказаться уже совсем другим. Такое поведение приведет к дополнительной нагрузке на приложения, которые используют для страниц резидентность NUMA. Блокировка страницы позволяет разрешить эту проблему, полностью защищая их от листания.

Второе свойство — захват рабочего множества и блокировка в базе данных только PFN, дает возможность приложениям работать быстрее и повышает масштабируемость пилообразной нагрузки.

В NUMA архитектуре, SQL Server Buffer Pool фиксирует каждую распределенную страницу с выделенным для неё узлом. Достигается этого за счёт использования QueryWorkingSetEx. Как только страница распределена, вызывается этот API, который позволяет узнать детали резидентности страницы. Делается это только один раз. Поэтому включение locked pages для SQL Server на 64-х битной платформе улучшает работу с пилообразной нагрузкой и повышает производительность и масштабируемость на более длительных отрезках времени. При работе SQL Server в режиме locked pages, Вам не нужно больше волноваться о производительности системы в целом, зависимости от изъятия памяти у SQL Server, когда он участвует в механизме листания операционной системы, прослушивая оповещения отвечающего в системе за память API, и сокращая своё рабочее множество, когда это от него требуют.

Думаю, многие разработчики в своей практике сталкивались с такой неприятной исключительной ситуацией, как OutOfMemoryException. Чем она неприятна? Тем, что она свидетельствует об одной из двух вещей: либо ваше приложение скушало всю доступную память и сделало это ожидаемо, потому что вы его так запрограммировали, либо оно сделало это вследствие утечки памяти (memory leak). И первый, и второй случай чреваты серьезными проблемами. В первом случае нам нужно рефакторить код и, возможно, даже архитектуру с целью избежания этой ситуации в дальнейшем, и не факт, что при этом не придется придумывать какие-нибудь обходные методы. Во втором случае мы сталкиваемся с тем, что мы, скорее всего, понятия не имеем, где происходит утечка и как с этим бороться, то есть у нас налицо предстоящий достаточно сложный дебаг. И самое неприятное во всей этой ситуации то, что мы не знаем, с каким же все-таки вариантом мы имеет дело. А если не знаем, значит, самое время взять скальпель стетоскоп и прослушать пациента.

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

Используем встроенные средства мониторинга ОС

На втором шаге мы пробуем минимальными усилиями разобраться с тем, что у нас происходит с памятью в процессе и сколько памяти у нас выделяется под сессию. Сделать это очень просто при помощи стандартных счетчиков производительности (performance counters) Windows. Нам пригодятся следующие как минимум счетчики, но вы можете воспользоваться и расширенным списком (помните, что включать их нужно для вашего процесса aspnet_wp/w3wp):

Выглядеть это будет примерно так:


Настоятельно рекомендую перед работой с Task Manager прочитать пост Tess. В частности, там можно прочитать вот такую интересную вещь:

"If you want to see this in action, you can create a winforms application and allocate a bunch of objects and see the working set go up, and then if you minimize the app, the working set drops. This doesn't by any means mean that you have just released all this memory. It just means that you are looking at a counter that is totally irrelevant for determining how much stuff you store in memory :) Yet. this is the counter that people most often look at to determine memory usage.

I know that by now you are probably thinking "yeah right", you haven't even heard of this counter before, why would I say that this is the counter most people look at. The answer is, because most people use task manager to look at memory usage of a process, and specifically look at the Memory Usage column. Surprise surprise:) what this actually shows you is the working set of the process.

If you want to see private bytes which is a far more interesting counter, you should look at the column in task manager that is labeled Virtual Memory Size (yeah, that's really intuitive:)), or better yet, look in performance monitor at process\private bytes and process\virtual bytes, there is no reason not to if your intent is to investigate high memory usage or a memory leak. "

Так что будьте осторожны ;)

Знакомимся с тяжелой артиллерией

После всех указанных действий вы увидите картинку наподобие следующей:


Анализируем дамп процесса

Теперь подключаем sos.dll к WinDbg при помощи команды:


Как видите, команд здесь довольно много. Я не буду останавливаться на всех них. Подробное описание процесса инсталляции и команд можно найти в секции Debugging School блога Johan Straarup:

Теперь, когда мы знаем минимально необходимый набор команд, я покажу приблизительный алгоритм поиска источника проблемы.

Начнем мы с анализа памяти. Проверим, что у нас вообще творится в куче:

Здесь мы тоже видим довольно много интересного. Во-первых, для будущих тестов нам нужны MT (что-то типа идентификатора типа объекта) объектов System.Web.Caching.Cache и System.Web.SessionState.InProcSessionState, которые соответствуют глобальному кешу приложения и его сессиям, которые наравне с другими объектами кеша хранятся в нем. Еще мы видим, что у нас очень много объектов хранится в массивах строк, но толку нам пока от этого немного. Вот если бы там были какие-то более специфические объекты, например, DataTable, мы могли бы туда сходить и посмотреть более серьезно. А так глянем сначала, сколько у нас занимается кеш в целом и сессии в частности. Для этого сначала нужно найти адреса этих объектов:

0:000> !dumpheap -mt 6639d878
------------------------------
Heap 0
Address MT Size
total 0 objects
------------------------------
Heap 1
Address MT Size
total 0 objects
------------------------------
Heap 2
Address MT Size
total 0 objects
------------------------------
Heap 3
Address MT Size
total 0 objects
------------------------------
Heap 4
Address MT Size
total 0 objects
------------------------------
Heap 5
Address MT Size
total 0 objects
------------------------------
Heap 6
Address MT Size
0e961294 6639d878 12
total 1 objects
------------------------------
Heap 7
Address MT Size
total 0 objects
------------------------------
total 1 objects
Statistics:
MT Count TotalSize Class Name
6639d878 1 12 System.Web.Caching.Cache
Total 1 objects
0:000> !objsize 0e961294
sizeof(0e961294) = 336106272 ( 0x14089320) bytes (System.Web.Caching.Cache)
0:000> !dumpheap -mt 663b0cdc
------------------------------
Heap 0
Address MT Size
0355a598 663b0cdc 48
03969aa0 663b0cdc 48
039711e4 663b0cdc 48
total 3 objects
------------------------------
Heap 1
Address MT Size
total 0 objects
------------------------------
Heap 2
Address MT Size
07f62150 663b0cdc 48
total 1 objects
------------------------------
Heap 3
Address MT Size
08f53ec8 663b0cdc 48
total 1 objects
------------------------------
Heap 4
Address MT Size
total 0 objects
------------------------------
Heap 5
Address MT Size
total 0 objects
------------------------------
Heap 6
Address MT Size
total 0 objects
------------------------------
Heap 7
Address MT Size
10d01948 663b0cdc 48
total 1 objects
------------------------------
total 6 objects
Statistics:
MT Count TotalSize Class Name
663b0cdc 6 288 System.Web.SessionState.InProcSessionState
Total 6 objects

Итак, кеш весит 336 Мб, а сессий у нас 6 штук. Берем первую попавшуюся и смотрим ее размер:

0:000> !objsize 0355a598
sizeof(0355a598) = 335808888 ( 0x14040978) bytes (System.Web.SessionState.InProcSessionState)

Посмотрим, что у нас хранится в сессии:

А теперь посмотрим, кто на кого ссылается:

Выводы из данной ситуации очень простые:

Да и вообще, эти блоге стоит добавить в ваш персональный RSS feeder. Можно вместе с блогом Марка Руссиновича (ага, это тот, который написал кучу полезных системных тулов, известных под названием Sysinternals). Пригодятся.

Читайте также: