2015-11-27 2 views
1

Иногда наши клиенты наблюдают исключение из памяти в нашем приложении. Поскольку мы регистрируем их действия, мы можем грубо воспроизвести то, что они сделали, но если я это сделаю и профилирую приложение с помощью dotMemory, я не могу воспроизвести исключение, а используемая память (около 100 МБ управляемых + 500 МБ неуправляемых) намного меньше предела (2 ГБ, поскольку это 32-битное приложение). Кроме того, в момент, когда исключение поймано, текущее использование памяти запрашивается с помощью Process.GetCurrentProcess(). WorkSet64, который указывает на использование памяти между 500 и 900 МБ. Я знаю, что это число не очень надежное, но это еще один признак того, что должно быть достаточно памяти.исключение из памяти, хотя доступно достаточное количество памяти

Соответствующим свойством приложения является то, что он относится к временным рядам измерений (пары DateTime и double хранятся в массиве). Эти объекты могут быть достаточно большими для хранения в большой куче объекта (LOH). Таким образом, фрагментация кучи действительно имеет место, но при профилировании это делало не, похоже, большое дело. Размер LOH составлял менее 100 МБ , включая отверстия.

Возможно ли, что сборщик мусора (GC) вызывается после исключения исключения из памяти? Я думаю, что в случае неудовлетворенного запроса на выделение памяти исключение генерируется только в том случае, если GC не может собрать достаточное количество памяти. Но, возможно, это отличается от памяти, выделенной в LOH, по сравнению с памятью, выделенной в куче генерации 0?

У кого-нибудь есть идея, как мы могли бы решить эту проблему?

Мы используем VS 2010 SP1 и .NET 4.0. Вопрос может быть связан с поднятым вопросом here, here и here, но я не нашел удовлетворительного ответа там.

Обновление: Добавлен образцовый трассировки стека и график кучи fragementation

Там не единственное место, где из-за нехватки памяти исключения срабатывают, но так как это было предложено, добавить STRACK след :

Exception of type 'System.OutOfMemoryException' was thrown. 
mscorlib 
    at System.Runtime.Serialization.ObjectIDGenerator.Rehash() 
    at System.Runtime.Serialization.ObjectIDGenerator.GetId(Object obj, Boolean& firstTime) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.InternalGetId(Object obj, Boolean assignUniqueIdToValueType, Type type, Boolean& isNew) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Schedule(Object obj, Boolean assignUniqueIdToValueType, Type type, WriteObjectInfo objectInfo) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteMembers(NameInfo memberNameInfo, NameInfo memberTypeNameInfo, Object memberData, WriteObjectInfo objectInfo, NameInfo typeNameInfo, WriteObjectInfo memberObjectInfo) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteMemberSetup(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, String memberName, Type memberType, Object memberData, WriteObjectInfo memberObjectInfo) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, String[] memberNames, Type[] memberTypes, Object[] memberData, WriteObjectInfo[] memberObjectInfos) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo) 
    at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) 
    ... <methods from our application follow> 

следующая диаграмма из dotMemory изображает фрагментации LOH после работы в течение около часа с инструментом:

enter image description here

+0

@ ThomasAndreèLian - вы связали их с вопросом, что происходит с .NET 1.1 до установки пакета обновления 1 применяется к нему. Поскольку в их заявлении говорится, что они используют .NET 4.0, и любой .NET predating 3.5 не поддерживается, вы думаете, что это вероятно? –

+0

@ Damien_The_Unbeliever, вы совершенно правы, я должен быть медленным сегодня, только прочитав симптомы, и он подходит. –

+0

@Damien_The_Unbeliever - Исключения не выбрасываются из любого класса System.Drawing. Они возникают во время сериализации, например, или в других местах, где запрашивается значительный объем памяти. – MarkusParker

ответ

2

Использование инструмента vmmap Я нашел причину проблемы: фактическая память, доступная для управляемой кучи, намного меньше предела 2 ГБ. Это несколько разделяемых библиотек, загруженных для взаимодействия с инструментами MS Office (~ 400 МБ). Существуют также собственные DLL-файлы (~ 300 МБ), которые также выделяют неуправляемую кучу (~ 300 МБ). Есть и много других вещей, и, в конце концов, только около 700 МБ остаются за управляемой кучей.

Поскольку объем памяти намного меньше, чем я изначально думал, фрагментация LOH может иметь большее влияние, чем я подозревал и действительно: vmmap показывает, что наибольший свободный блок в этой области памяти становится меньше по таймеру, хотя доступный память остается неизменной. Я думаю, это доказывает, что фрагментация является причиной проблемы. Триггером исключения часто является двоичная сериализация, которую мы иногда используем для глубоких копирующих объектов. Это, по-видимому, вызывает пик в использовании памяти.

А что с этим делать?Я рассматриваю следующие варианты:

  • Переключить на x64 (которые случатся в конечном счете, так или иначе)
  • Переключение в .NET 4.5.1, которая позволяет defragment в LOH
  • уменьшить общее потребление памяти: Похоже, что дефрагментация занимает гораздо больше времени, если дополнительно доступно 200 МБ. Это происходит, когда некоторые большие библиотеки не загружаются. В моих экспериментах я больше не мог вызывать исключение из памяти.
  • Изменить код, который, вероятно, будет слишком много времени
+0

Не могли бы вы использовать пул памяти для устранения фрагментации? – Jon

+0

Я думаю, да, но разве это не означало бы переопределение основных частей управления памятью C#? Я боюсь, что это слишком много усилий. – MarkusParker

Смежные вопросы