Подтвердить что ты не робот

Переполнение памяти: количество пользователей Microsoft.CSharp.RuntimeBinder.Semantics

В настоящее время мы выполняем некоторые утечки памяти в нашем приложении, выполняя некоторую операцию (загружая и закрывая один проект внутри нашего приложения), мы знаем, что увеличение памяти всегда немного.

Мы уже много их нашли, но теперь 10 + наиболее растущие классы (согласно нашему инструменту, ANTS Memory Profiler 8.2):

  • Microsoft.CSharp.RuntimeBinder.Semantics.SYMTBL + Key
  • Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbol
  • Microsoft.CSharp.RuntimeBinder.Semantics.CONSTVAL
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCONSTANT
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCLASS
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRTYPEOF
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRLIST
  • Microsoft.CSharp.RuntimeBinder.Semantics.MethWithInst
  • Microsoft.CSharp.RuntimeBinder.Semantics.CMemberLookupResults
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRMEMGRP
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCALL
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRWRAP
  • Microsoft.CSharp.RuntimeBinder.Semantics.AggregateDeclaration
  • Microsoft.CSharp.RuntimeBinder.Semantics.Scope

К сожалению, я не знаю, что это такое, поэтому мне немного сложно найти, как/что я должен отпустить.

Я проверил дерево экземпляров, но он полностью подходит к материалам microsoft.

Проблема в том, что когда мы выполняем "Open/close" проекта, мы проходим много (большинство) нашего кода.

EDIT Одна часть нашего приложения использует ключевое слово dynamic для некоторых ресурсов, оно может быть связано. Класс здесь не одноразовый, должен ли я делать с ними что-то особенное?

РЕДАКТИРОВАТЬ 2

Я уверен, что это связано с моим материалом dynamic, похоже, что С# создает кеш при использовании динамического. Но в настоящее время я понятия не имею, почему он растет (я загружаю одни и те же классы все время, и я буду иметь точно такую ​​же подпись все время), и как очистить это.

4b9b3361

Ответ 1

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

На основе вашего приложения лучше всего тщательно подумать, можете ли вы разработать свое решение, чтобы избежать динамического. Ниже приведены некоторые допустимые варианты использования для динамических: https://msdn.microsoft.com/en-us/library/dd264736.aspx

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

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

PS: Кроме того, публикация некоторых фрагментов кода помогает.

Ответ 2

Сегодня я столкнулся с такой же проблемой, профайлируя утечки памяти в своем приложении RepoZ. Этот инструмент должен работать в фоновом режиме, периодически проверяя репозитории Git и периодически обновляя заголовки Windows Explorer. Последняя задача должна сделать некоторые вызовы COM для "Shell.Application", чтобы найти окна проводника и определить путь, на который они указывают в настоящее время.

Используя ключевое слово dynamic, подобное этому...

dynamic shell = Activator.CreateInstance(...);
foreach (object window in shell.Windows())
{ 
    var hwnd = window.Hwnd;
    ...
}

... Через несколько часов я дошел до дампа памяти:

введите описание изображения здесь

ComBridge

Чтобы решить эту проблему, я написал небольшой вспомогательный класс под названием "Комбридж" , заботящийся о выпуске COM-объектов и обеспечивающий довольно легкий доступ к методам и свойства базового COM-объекта. Это очень легко и просто, ничего особенного здесь. Он использует Отражение для объектов COM, поэтому существует некоторая потеря производительности (см. Ниже).

С ним образец кода сверху выглядит следующим образом:

using (var shell = new Combridge(Activator.CreateInstance(...)))
{
    var windows = shell.InvokeMethod<IEnumerable>("Windows");
    foreach (var window in windows)
    {
        var hwnd = window.GetPropertyValue<long>("Hwnd");
        ... 
    }
}

Вы можете увидеть файл ExplorerWindowActor о том, как он используется в RepoZ.

Это не так красиво, как с dynamic, и в этой первой попытке производительность ухудшилась. Быстрая скамья показала следующее:

Производительность

Я протестировал 1000 итераций, на каждой итерации обработано 10 открытых окон Explorer. Для каждого окна на этот COM-объект вызывается 4 метода или свойства. Итак, мы говорим о 40 000 COM-вызовах.

Продолжительность возрастала от ~ 2500 мс (dynamic) до ~ 6000 мс (Combridge). Это от 0.062ms до 0.150ms для каждого вызова.

Таким образом, это займет около 2.4x времени для завершения.

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

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

~ ~ Update

Через 10 часов RepoZ все еще работает с очень постоянной памятью.

введите описание изображения здесь

Таким образом, при открытии 10 окон Explorer, 4 COM-вызовов за окно и весь цикл два раза в секунду, RepoZ создал около 72.000 COM-экземпляров и сделал около 2.880.000 COM-вызовов в целом без увеличения потребления памяти.

Я думаю, мы можем сказать, что проблема действительно связана с dynamic.