Недавно я обнаружил некоторые утечки памяти в .NET (то есть неожиданные, затянувшиеся объекты, связанные с GC) в приложении WinForms. После загрузки, а затем закрытия огромного отчета использование памяти не уменьшилось, как ожидалось, даже после нескольких сборок поколения 2. Предполагая, что управляющий отчет поддерживал случайный обработчик событий, я открыл WinDbg, чтобы посмотреть, что происходит ...
Используя WinDbg, команда !dumpheap -stat
сообщила, что строковые экземпляры использовали большой объем памяти. Далее, уточнив это с помощью команды !dumpheap -type System.String
, я нашел виновника, строку размером 90 МБ, используемую для отчета, по адресу 03be7930. Последний шаг состоял в том, чтобы вызвать !gcroot 03be7930
, чтобы увидеть, какие объекты поддерживали его.
Мои ожидания были неверны - это был не обработчик событий с отцепленными цепями, висящий на элементе управления отчетами (и строке отчета), но вместо этого он удерживался экземпляром System.Text.RegularExpressions.RegexInterpreter
, который сам является потомком System.Text.RegularExpressions.CachedCodeEntry
. Теперь кеширование регулярных выражений является (в некоторой степени) общеизвестным, поскольку это помогает сократить накладные расходы, связанные с перекомпиляцией регулярных выражений при каждом его использовании. Но какое отношение это имеет к поддержанию моей струны?
На основании анализа с использованием Reflector выясняется, что входная строка сохраняется в RegexInterpreter при каждом вызове метода Regex. RegexInterpreter удерживает эту строковую ссылку, пока новая строка не будет введена в него последующим вызовом метода Regex. Я ожидал бы подобного поведения, если зависел от экземпляров Regex.Match и, возможно, от других. Цепочка примерно такая:
- Regex.Split, Regex.Match, Regex.Replace и т. Д.
- Regex.Run
- RegexScanner.Scan (RegexScanner - базовый класс, RegexInterpreter - описанный выше подкласс).
Regex-нарушитель используется только для отчетов, редко используется и, следовательно, вряд ли будет использоваться снова для очистки существующей строки отчета. И даже если бы Regex использовался позднее, он, вероятно, обработал бы еще один большой отчет. Это довольно значительная проблема, и просто она кажется грязной.
Все это говорит о том, что я нашел несколько вариантов того, как разрешить или, по крайней мере, обойти этот сценарий. Сначала я позволю сообществу ответить, а если не получит никаких откликов, я заполню все пробелы через день или два.