Наблюдая за счетчиком производительности CLR #Bytes in all Heaps
нового приложения .NET 4.5 сервера за последние несколько дней, я могу заметить шаблон, который заставляет меня думать, что коллекция Gen2 не всегда собирает мертвые объекты, но у меня возникают проблемы понимая, что именно происходит.
Серверное приложение работает в .NET Framework 4.5.1 с использованием GC GC/Background.
Это консольное приложение, размещенное как служба Windows (с помощью рамок Topshelf)
Серверное приложение обрабатывает сообщения, а пропускная способность как-то довольно постоянна.
Что я вижу, глядя на график CLR #Bytes in all Heaps
, так это то, что память начала около 18 МБ, а затем увеличилась до 35 МБ примерно за 20-24 часа (с 20-30 коллекциями Gen2 в течение этого временного интервала), а затем все внезапное падение до номинального значения 18 МБ, а затем рост до ~ 35 МБ в течение 20-24 часов и падение до 18 МБ и т.д. (я вижу, что образец повторяется в течение последних 6 дней, когда приложение запущено)... Рост памяти не является линейным, требуется около 5 часов для роста на 10 МБ, а затем 15-17 часов для оставшихся 10 МБ или около того.
Дело в том, что я вижу, глядя на счетчики perfmon для #Gen0/#Gen1/#Gen2 collections
, что куча коллекций Gen2 происходит в течение 20-24 часов (может быть, около 30), и ни одна из них не возвращает память к номинальному 18MB.
Однако странным является использование внешнего инструмента для принудительного GC (Perfview в моем случае), тогда я вижу, что #Induced GC
растет на 1 (GC.Collect был вызван так, что это нормально), и сразу же идет память назад к номинальному 18 МБ.
Что заставляет меня думать, что либо счетчик perfmon для коллекций # Gen2 не прав, и только одна коллекция Gen2 происходит через 20-22 часа или около того (meeehhh я действительно так не думаю) или что коллекция Gen2 не всегда собирать мертвые объекты (кажется более правдоподобным)... но в этом случае почему бы заставить GC через GC.Collect сделать трюк, какая разница между явным вызовом в GC.Collect, а также автоматически сгенерированные коллекции во время жизни приложения.
Я уверен, что есть очень хорошее объяснение, но из другого источника документации, который я нашел о GC -too few: (- коллекция Gen2 собирает мертвые объекты в любом случае. Поэтому, возможно, документы не актуальны или Я неправильно понял... Любое объяснение приветствуется. Спасибо!
РЕДАКТИРОВАТЬ: Смотрите этот снимок экрана #Bytes in all heaps
в течение 4 дней
(Нажмите, чтобы увеличить)
это проще, чем пытаться графовать вещи в голове. То, что вы можете видеть на графике, - это то, что я сказал выше... память увеличилась за 20-24 часа (и 20-30 коллекций Gen2 в течение этого временного интервала), пока не достигнет ~ 35 МБ, а затем внезапно опустится. В конце графика вы заметите, что индуцированный GC я запускается через внешний инструмент, немедленно отбрасывая память на номинальную.
EDIT # 2: Я сделал много очистки в коде, в основном в отношении финализаторов. У меня было много классов, которые ссылались на одноразовые типы, поэтому мне пришлось реализовать IDisposable
для этих типов. Однако в некоторых случаях я вводил в заблуждение некоторые статьи о реализации шаблона Diposable с Finalizer. Прочитав некоторую документацию MSDN, я понял, что финализатор необходим только тогда, когда тип сам хранит собственные ресурсы (и в этом случае этого можно избежать с помощью SafeHandle). Поэтому я удалил все финализаторы из всех этих типов. В коде были некоторые другие модификации, но в основном бизнес-логика, ничего ".NET framework" не было связано.
Теперь график очень отличается, это плоская линия вокруг 20 МБ в течение нескольких дней... точно, что я ожидал увидеть!
Итак, проблема теперь исправлена, однако я до сих пор не знаю, в чем проблема из-за... Похоже, что это могло быть связано с финализаторами, но все же не объясняет, что я заметил, даже если мы не вызывали Dispose (true) - подавление финализатора - поток финализатора должен пинаться между коллекцией, а не каждые 20-24 часа?!
Учитывая, что мы сейчас отошли от проблемы, потребуется время, чтобы вернуться к "багги" и воспроизвести ее снова. Я могу попытаться сделать это некоторое время, хотя и пойти на дно.
EDIT: Добавлен граф коллекции Gen2 (Нажмите для увеличения)