exception of type system outofmemoryexception was thrown что за ошибка

Исправление: Возникает исключение System.OutOfMemoryException или IDE отвечает медленно после построения решения, содержит несколько раз во многих проектах WPF с помощью платформа.NET Framework 3.5 SP1

Симптомы

Создавать сложные решения, содержащего много проектов Windows Presentation Foundation (WPF), основанных на Microsoft платформа.NET Framework 3.5 Пакет обновления 1 (SP1). При каждом построении решения, увеличивается использование памяти процесса devenv.exe. После построения решения несколько раз появиться одно или несколько из следующих проблем:

Исключение типа «System.OutOfMemoryException».

При нажатии любой кнопки в Интегрированной среде разработки IDE медленно реагирует и даже сбоям.

Примечание. Данная проблема возникает даже в том случае, когда XAML-файлы не открыть.

Причина

Эта проблема возникает из-за фрагментации памяти между библиотекой классов платформа.NET Framework и средства синтаксического анализа XAML.

Решение

Сведения об исправлении

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

Если исправление доступно для скачивания, имеется раздел «Пакет исправлений доступен для скачивания» в верхней части этой статьи базы знаний. Если этот раздел не отображается, обратитесь в службу поддержки для получения исправления.

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

http://support.microsoft.com/contactus/?ws=supportПримечание. В форме «Пакет исправлений доступен для скачивания» отображаются языки, для которых доступно исправление. Если нужный язык не отображается, значит исправление для данного языка отсутствует.

Предварительные условия

Платформа.NET Framework 3.5 с пакетом обновления 1 для установки этого исправления необходимо иметь.

Необходимость перезагрузки

Необходимо перезагрузить компьютер после установки исправления, если используется не экземпляр платформа.NET Framework.

Сведения о замене исправлений

Это исправление не заменяет других исправлений.

Сведения о файлах

Английская версия данного исправления содержит атрибуты файла (или более поздние атрибуты файлов), приведенные в следующей таблице. Дата и время для этих файлов указаны в формате общего скоординированного времени (UTC). При просмотре сведений о файле, он преобразуется в локальное время. Чтобы узнать разницу между временем по Гринвичу и местным временем, откройте вкладку Часовой пояс элемента Дата и время в панели управления.

Источник

Windraw dot Net

Победа OutOfMemoryException

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

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

1. Использование метода dbo.ZipUnPack в запросах(то есть на стороне SQL Server) построителя отчетов Stimulsoft Reports.Net приводил к тому, что ошибка System.OutOfMemoryException появлялась гораздо чаще. Переведя выполнение этого метода на сторону сервера приложений (т.е. используя Atechnology.Components.ZipArchiver.UnZip2 (byte[] classnative) ) мы значительно увеличили время работы SQLServer без перезагрузки.

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, хотя оперативная память используется практически полностью!

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

Читайте также:  izotope meter tap что это

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

Источник

Exception of type ‘System.OutOfMemoryException’ was thrown. Why?

The error is happening here:

Here is the code the runs on the button click:

5 Answers 5

It runs successfully the first time, but if I run it again, I keep getting a System.OutOfMemoryException. What are some reasons this could be happening?

Regardless of what the others have said, the error has nothing to do with forgetting to dispose your DBCommand or DBConnection, and you will not fix your error by disposing of either of them.

The error has everything to do with your dataset which contains nearly 600,000 rows of data. Apparently your dataset consumes more than 50% of the available memory on your machine. Clearly, you’ll run out of memory when you return another dataset of the same size before the first one has been garbage collected. Simple as that.

You can remedy this problem in a few ways:

Consider returning fewer records. I personally can’t imagine a time when returning 600K records has ever been useful to a user. To minimize the records returned, try:

Limiting your query to the first 1000 records. If there are more than 1000 results returned from the query, inform the user to narrow their search results.

If your users really insist on seeing that much data at once, try paging the data. Remember: Google never shows you all 22 bajillion results of a search at once, it shows you 20 or so records at a time. Google probably doesn’t hold all 22 bajillion results in memory at once, it probably finds its more memory efficient to requery its database to generate a new page.

If you just need to iterate through the data and you don’t need random access, try returning a datareader instead. A datareader only loads one record into memory at a time.

Remove all references to your old dataset. Anything holding on to a refenence of your dataset will prevent it from being reclaimed by memory.

If you can’t null all the references to your dataset, clear all of the rows from the dataset and any objects bound to those rows instead. This removes references to the datarows and allows them to be eaten by the garbage collector.

Note: calling Dispose on your dataset does not free any memory, nor does it invoke the garbage collector, nor does it remove a reference to your dataset. Dispose is used to clean up unmanaged resources, but the DataSet does not have any unmanaged resources. It only implements IDispoable because it inherents from MarshalByValueComponent, so the Dispose method on the dataset is pretty much useless.

Источник

Out OfMemory Exception Class

Definition

Some information relates to prerelease product that may be substantially modified before it’s released. Microsoft makes no warranties, express or implied, with respect to the information provided here.

The exception that is thrown when there is not enough memory to continue the execution of a program.

Remarks

For a list of initial property values for an instance of OutOfMemoryException, see the OutOfMemoryException constructors.

An OutOfMemoryException exception has two major causes:

You are attempting to expand a StringBuilder object beyond the length defined by its StringBuilder.MaxCapacity property.

The common language runtime cannot allocate enough contiguous memory to successfully perform an operation. This exception can be thrown by any property assignment or method call that requires a memory allocation. For more information on the cause of the OutOfMemoryException exception, see the blog post «Out of Memory» Does Not Refer to Physical Memory.

This type of OutOfMemoryException exception represents a catastrophic failure. If you choose to handle the exception, you should include a catch block that calls the Environment.FailFast method to terminate your app and add an entry to the system event log, as the following example does.

Some of the conditions under which the exception is thrown and the actions you can take to eliminate it include the following:

You are calling the StringBuilder.Insert method.

You are attempting to increase the length of a StringBuilder object beyond the size specified by its StringBuilder.MaxCapacity property. The following example illustrates the OutOfMemoryException exception thrown by a call to the StringBuilder.Insert(Int32, String, Int32) method when the example tries to insert a string that would cause the object’s Length property to exceed its maximum capacity.

You can do either of the following to address the error:

Replace the call to the StringBuilder.StringBuilder(Int32, Int32) constructor with a call any other StringBuilder constructor overload. The maximum capacity of your StringBuilder object will be set to its default value, which is Int32.MaxValue.

Call the StringBuilder.StringBuilder(Int32, Int32) constructor with a maxCapacity value that is large enough to accommodate any expansions to the StringBuilder object.

Your app runs as a 32-bit process.

32-bit processes can allocate a maximum of 2GB of virtual user-mode memory on 32-bit systems, and 4GB of virtual user-mode memory on 64-bit systems. This can make it more difficult for the common language runtime to allocate sufficient contiguous memory when a large allocation is needed. In contrast, 64-bit processes can allocate up to 8TB of virtual memory. To address this exception, recompile your app to target a 64-bit platform. For information on targeting specific platforms in Visual Studio, see How to: Configure Projects to Target Platforms.

Your app is leaking unmanaged resources

If you are consuming a type that uses unmanaged resources, you should be sure to call its IDisposable.Dispose method when you have finished using it. (Some types also implement a Close method that is identical in function to a Dispose method.) For more information, see the Using Objects That Implement IDisposable topic.

If you have created a type that uses unmanaged resources, make sure that you have implemented the Dispose pattern and, if necessary, supplied a finalizer. For more information, see Implementing a Dispose method and Object.Finalize.

You are attempting to create a large array in a 64-bit process

You are working with very large sets of data (such as arrays, collections, or database data sets) in memory.

When data structures or data sets that reside in memory become so large that the common language runtime is unable to allocate enough contiguous memory for them, an OutOfMemoryException exception results.

To prevent the OutOfMemoryException exceptions, you must modify your application so that less data is resident in memory, or the data is divided into segments that require smaller memory allocations. For example:

If you are retrieving all of the data from a database and then filtering it in your app to minimize trips to the server, you should modify your queries to return only the subset of data that your app needs. When working with large tables, multiple queries are almost always more efficient than retrieving all of the data in a single table and then manipulating it.

If you are executing queries that users create dynamically, you should ensure that the number of records returned by the query is limited.

Читайте также:  что делать если windows 10 не запускается после обновления

If you are using large arrays or other collection objects whose size results in an OutOfMemoryException exception, you should modify your application to work the data in subsets rather than to work with it all at once.

The following example gets a array that consists of 200 million floating-point values and then calculates their mean. The output from the example shows that, because the example stores the entire array in memory before it calculates the mean, an OutOfMemoryException is thrown.

The following example eliminates the OutOfMemoryException exception by processing the incoming data without storing the entire data set in memory, serializing the data to a file if necessary to permit further processing (these lines are commented out in the example, since in this case they produce a file whose size is greater than 1GB), and returning the calculated mean and the number of cases to the calling routine.

You are repeatedly concatenating large strings.

Because strings are immutable, each string concatenation operation creates a new string. The impact for small strings, or for a small number of concatenation operations, is negligible. But for large strings or a very large number of concatenation operations, string concatenation can lead to a large number of memory allocations and memory fragmentation, poor performance, and possibly OutOfMemoryException exceptions.

When concatenating large strings or performing a large number of concatenation operations, you should use the StringBuilder class instead of the String class. When you have finished manipulating the string, convert the StringBuilder instance to a string by calling the StringBuilder.ToString method.

You pin a large number of objects in memory.

Pinning a large number of objects in memory for long periods can make it difficult for the garbage collector to allocate contiguous blocks of memory. If you’ve pinned a large number of objects in memory, for example by using the fixed statement in C# or by calling the GCHandle.Alloc(Object, GCHandleType) method with a handle type of GCHandleType.Pinned, you can do the following to address the OutOfMemoryException exception.

Evaluate whether each object really needs to be pinned,

Ensure that each object is unpinned as soon as possible.

Make sure that each call to the GCHandle.Alloc(Object, GCHandleType) method to pin memory has a corresponding call to the GCHandle.Free method to unpin that memory.

The following Microsoft intermediate (MSIL) instructions throw an OutOfMemoryException exception:

Constructors

Initializes a new instance of the OutOfMemoryException class.

Initializes a new instance of the OutOfMemoryException class with serialized data.

Initializes a new instance of the OutOfMemoryException class with a specified error message.

Initializes a new instance of the OutOfMemoryException class with a specified error message and a reference to the inner exception that is the cause of this exception.

Properties

Gets a collection of key/value pairs that provide additional user-defined information about the exception.

Gets or sets a link to the help file associated with this exception.

Gets or sets HRESULT, a coded numerical value that is assigned to a specific exception.

Gets the Exception instance that caused the current exception.

Gets a message that describes the current exception.

Gets or sets the name of the application or the object that causes the error.

Gets a string representation of the immediate frames on the call stack.

Gets the method that throws the current exception.

Methods

Determines whether the specified object is equal to the current object.

When overridden in a derived class, returns the Exception that is the root cause of one or more subsequent exceptions.

Serves as the default hash function.

When overridden in a derived class, sets the SerializationInfo with information about the exception.

Gets the runtime type of the current instance.

Creates a shallow copy of the current Object.

Creates and returns a string representation of the current exception.

Events

Occurs when an exception is serialized to create an exception state object that contains serialized data about the exception.

Источник

Блог Александра Кондуфорова

об информационных технологиях, программировании, путешествиях и фотографии

Thursday, August 28, 2008

OutOfMemoryException и как его побороть

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

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

Первый шаг очень простой: необходимо собрать как можно больше информации. Для начала общаемся с QA, которые нашли баг и пытаемся выяснить условия, при которых воспроизводится баг. OutOfMemoryException – исключение подлое, в большинстве случаев оно не воспроизводится в лоб, а просто вылетает в какой-то момент жизни приложения, когда память переполнится. Полученная информация нам поможет и для воспроизведения бага, и для собственного понимания того, что творится внутри процесса. В частности, у нас вот проблема в основном проявлялась при большом количестве пользователей, что неудивительно, так как сессии довольно большие, но все равно странно, так как непонятно, чем же занимается в таком случае сборщик мусора? Ну, о причинах этого потом – в примере.

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

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

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

Собственно, здесь мы хотим посмотреть размеры куч всех трех поколений и LOH, а также общую занятую память и committed/reserved bytes. Reserved bytes показывает, сколько наше приложение зарезервировало себе памяти, committed – сколько оно реально из этого количества уже использует. Отличное описание этих и других полезных счетчиков и их назначение можно найти здесь. Итак, запускаем счетчики и Windows Task Manager (с ним будем отслеживать Mem usage и VM usage для процесса aspnet_wp/w3wp), заполняем себе табличку в Excel – и поехали.

Настоятельно рекомендую перед работой с 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. «

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

Логинимся в приложение одним пользователем, делаем замеры, логинимся следующим, делаем замеры. По пути можно попробовать просмотреть разную функциональность системы, чтобы увидеть, где именно мы получаем большое увеличение памяти. По размерам куч видим, как постепенно увеличивается количество памяти, и что у нас чистится GC, а что – нет. По Mem usage и VM usage определяем общее количество используемой памяти и пытаемся путем несложных арифметических операций определить, сколько же все-таки у нас занимает один пользователь в сессии. В нашем случае уже на 10 пользователях приходит понимание, что общее количество памяти растет непомерно и освобождаться почему-то не хочет. Ну, это логично – созданные сессии еще активны и сборщик мусора их собрать не может. Ставим опыт: отпускаем приложение на уровне 700 Мб и уходим на полчаса пить чай. Через полчаса заходим на сайт новым пользователем и делаем очередной замер. Нет, почти ничего не изменилось, используемая память продолжает увеличиваться, хотя должна была существенно уменьшится. Размер одной сессии в среднем – 30-40 Мб, что слишком много. Итак, либо у нас по какой-то причине не очищаются сессии, либо утечка памяти в другом месте. Информации по-прежнему мало. Пора браться за более серьезные инструменты анализа.

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

В роли тяжелой артиллерии может выступать практически любой performance tool, который умеет нормально мониторить память. Тулов много, но толковую информацию предоставляют единицы. Лично я все не смотрел, но вот мой любимый dotTrace, который не раз помогал мне находить performance-проблемы в коде, здесь полностью облажался. Ничего толкового из него я добиться не смог, может, просто руки кривые 🙂 Поэтому, если у вас нет особых вариантов, то советую сразу же взяться за WinDbg. WinDbg – это универсальный дебаггер, которая позволяет отлаживать любые win-приложения. А самое классное в нем то, что он позволяет не только подключаться к приложению и отлаживать в онлайн-режиме, но умеет еще и анализировать т.н. user dump’ы, то есть снимки внутреннего состояния приложения. Сделать эти снимки можно при помощи тулы User Mode Process Dumper, а WinDbg можно найти в пакете Debugging Tools for Windows. Итак, выполним подготовительные действия по шагам:

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

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

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

.load C:\ \Microsoft.NET\ Framework\v2.0.50727\sos.dll

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

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

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

Я сознательно опустил некоторые не очень важные подробности. Главное, что мы отсюда видим – что у нас 8 куч (по количеству ядер, виртуальных, и не только) и общий размер этих куч составляет около полугигабайта, что есть немало. Значит, кто-то там живет. Теперь выведем статистику по хранящимся в куче объектам:

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

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

Смотрим следующую – размер тот же, причем до байта. Еще одну – аналогично. Явно что-то не так в датском королевстве. Суммарный размер 6 сессий будет явно превышать размер объекта кеша, чего не может быть в принципе, так как объект Cache содержит в себе сессии. Однако, здесь есть особенность, которая может объяснить эту нестыковку – дело в том, что sos при подсчете размера объекта включает в него и размер всех объектов, на которые данный объект ссылается. Значит, у нас где-то сессии друг на друга ссылаются. Вот только где и как? Кроме того, мы можем сделать еще один важный вывод: чистая информация, которую мы вносили в кеш руками, занимает очень мало памяти, что очень хорошо. Если бы разница была больше, то нужно было бы идти анализировать еще и закешированные данные.

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

Как мы видим, в сессии хранится лишь некий экземпляр класса Custom.Namespace.SomeClass, который содержит перечисленный набор полей. В принципе, это известно давно тем, кто приложение программировал 🙂 Колонка Value показывает значения ссылок, нолики, как несложно догадаться – это null. Теперь посмотрим размеры объекта model:

Итак, главный объект SomeAnotherClass весит около 335 Мб, в то время как вложенный в него OneMoreAnotherClass – всего 63 Мб. Возможно, другие объекты содержат остальные данные? Проверяем – нет, не так. Хотя в нашем случае даже проверять не нужно было – мы и так знаем свой код 🙂 Проверяем другие сессии аналогичным образом – там та же картина, что вполне ожидаемо. Итак, к чему мы пришли? Наш Custom.Namespace.SomeClass и вложенный в него Custom.Namespace.SomeAnotherClass содержится в каждой сессии, а то, что objsize показывает для него размер всех сессий, указывает, что на этом уровне у нас возможны проблемы.

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

А вот и корень зла. Если посмотреть этот trace, то можно увидеть, что поначалу у нас все замечательно, кеш, сессия, а потом начинаются странности. Откуда-то появляются ссылки через делегаты, а класс SomeAnotherClass вообще фигурирует дважды, причем по разным адресам. Однако подсказка тоже есть – Custom.ApproveActionWorkflow.WorkflowManager+WorkflowNotificationsSentDelegate. Немного подумав, вспоминаем, что у нас есть замечательный синглтон-класс WorkflowManager, который предназначен для взаимодействия между пользовательскими потоками и потоком WorkflowRuntime, и оповещает все сессии при помощи генерации события о том, что им пора бы обновить некоторые специфические данные из базы. И он, конечно же, подписывает все создаваемые сессии на свое событие. А потом держит эту ссылку бесконечно, таким образом не давая сборщику мусора собрать сессии. Ну все, разобрались. Теперь дело за малым: зарефакторить код, чтобы сессии сами опрашивали класс об изменениях и избавиться от OutOfMemoryException 🙂 Дело сделано.

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

1) Делегаты, как оказывается – звери совсем не безобидные, за ними тоже нужен глаз да глаз
2) Старайтесь внимательно реализовывать межсессионное взаимодействие, чтобы не допускать не только ссылок на объекты, но и на функции
3) Не забывайте про особенности работы Task Manager и то, что он реально показывает. Не нужно на него сильно полагаться
4) Если же у вас все-таки вылетает OutOfMemoryException, не отчаивайтесь. Вооружайтесь дампом и WinDbg – и вперед. Люди и не такие баги отлаживают 🙂
5) WinDbg подойдет и для отладки других сложных ситуаций. Вот, например, рассказ Юры Скалецкого и некоего JKealey. Там есть что почитать.

Ну, и напоследок я настоятельно вам советую следующие статьи из блога Tess и Johan’а по поводу отладки утечек памяти:

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

Источник

Сказочный портал