Высокий Процессор Или Как Сборщик Мусора Может Убить Производительность

Добрый день.

Около 2 недель назад наш инструмент мониторинга (NewRelic) начал обнаруживать большое количество сбоев сайта продолжительностью не более 1 минуты, но с очень высокой частотой.

Кроме того, визуально было заметно, что общая производительность веб-приложения ( Умбрако 6.1.6, .

net 4.0 ) упал.

Красные полоски на картинке — это наши водопады.



Высокий процессор или как сборщик мусора может убить производительность

Да, я оговорюсь.

Прежде чем мы все это узнали, был установлен новый модуль блога и, соответственно, блог компании был перенесен с Worldpress на Umbraco. В итоге имеем следующие входные данные: приложение стало хранить больше данных (намного больше) + был установлен сторонний модуль = High CPU.



В путь

Перед началом исследования было решено проверить Googe Analytics, чтобы убедиться, что количество пользователей не изменилось (в результате все осталось так же) + было решено провести нагрузочное тестирование для определения пропускной способности.

Здесь нас ждало полное разочарование; наше приложение умерло, когда 30 одновременные сеансы.

Сайт вообще не открывался через браузер.

И это было производство.



Шаг 1. Сбор дампов производительности под нагрузкой с использованием Инструменты диагностики отладки
1. Установите его на рабочий сервер.

2. Запускаем, создаем новое правило с типом «Производительность».



Высокий процессор или как сборщик мусора может убить производительность

3. Указываем, что дампы нужно собирать с помощью Performance Counters.

Высокий процессор или как сборщик мусора может убить производительность

4. В нашем случае выбираем % Processor Time, порог — 80%, продолжительность — 15 секунд.

Высокий процессор или как сборщик мусора может убить производительность

Это означает, что дампы будут собраны, если загрузка процессора составит более 80% в течение 15 секунд. 5. Изучение результатов Вещи, на которые следует обратить внимание, выделены красными прямоугольниками.



Высокий процессор или как сборщик мусора может убить производительность



Высокий процессор или как сборщик мусора может убить производительность



Высокий процессор или как сборщик мусора может убить производительность

А именно:

  • На момент составления дампов был запущен Garbage Collector (сначала я на него не обратил внимания);
  • Очень большой размер кучи;
  • Все 4 потока принадлежат сборщику мусора и съедают 100% ресурсов процессора.

Здесь хотелось бы обратить внимание на то, что сама проблема заключается не в GC, а в том, что неправильное распределение памяти приводит к его такой работе.



Немного теории

В GC наиболее трудоемкой генерацией является сборка мусора Gen 2 (которая вызывает сборки Gen 1 и Gen 0 соответственно).

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

Это означает, что чем чаще будет превышен порог, тем чаще будет запускаться сборка мусора.

Небольшой пример: Предположим, порог поколения Gen 2: 300 МБ.

За одну секунду GC может очистить: 100 МБ (Gen 2) Каждому новому пользователю в секунду выделяется: 10 МБ (в Gen 2) Если у нас 10 пользователей, то 10*10=100 Мб, поэтому проблем нет. Если у нас 40 пользователей, то каждую секунду выделяется 400 МБ, из-за чего происходит сбор хаоса (превышение порога) и так по возрастанию.

То есть, чем больше пользователей, тем больше памяти выделяется (по возрастанию), тем чаще вызывается сборка мусора с большим временным интервалом сбора.

В .

net 4.0 при сборке мусора всем потокам сборщика мусора присваивается наивысший приоритет. Это означает, что все ресурсы сервера будут выделены на сбор мусора и, кроме того, все остальные потоки (обработка входящих запросов) будут временно приостановлены до завершения сборки мусора.

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

Следовательно, мы можем сделать вывод: Причина – неправильное выделение большого объема памяти за короткие промежутки времени.

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



Шаг 2 – поиск предметов.

которые занимают больше всего памяти (профилирование памяти)

Для этого я использовал точкаМемори в качестве профилировщика памяти.

Запускаем dotMemory под нагрузкой и пытаемся сделать снимок памяти, когда ее объем начинает существенно расти.

(Зеленая область на изображении ниже — поколение 2.)

Высокий процессор или как сборщик мусора может убить производительность

Далее приступаем к анализу изображения.



Высокий процессор или как сборщик мусора может убить производительность

Наибольший объем памяти занимает HttpContext, DynanicNode, Узел .

Мы исключаем HttpContext, поскольку он хранит ссылки на объекты DynanicNode и Node. Далее будем группировать по поколениям, так как нам нужны только объекты поколения Gen 2.

Высокий процессор или как сборщик мусора может убить производительность

В поколении Gen 2 мы снова группируемся по доминаторам.



Высокий процессор или как сборщик мусора может убить производительность

Это позволяет на 100% найти нужные объекты, занимающие наибольший объем памяти.

Затем вам нужно поработать с конкретными экземплярами объекта, чтобы определить, что это за объект (идентификатор, свойства и т. д.).



Высокий процессор или как сборщик мусора может убить производительность

В этот момент стало ясно, какие данные были источником проблемы; оставалось только найти место, где оно было создано, и исправить его.



Шаг 3. Устраните проблему
В моем конкретном случае проблема заключалась в элементе управления, который формировал основную навигацию сайта.

Этого элемента управления не было в кеше, то есть он работал при каждом запросе страницы.

А конкретный «порей памяти» был связан с вызовом нативного метода Umbraco. DynamicNode.isAncestor() .

Как оказалось, чтобы определить родителя, метод поднимал в память всё дерево сайта.

Это подтвердило тот факт, что проблема стала проявляться только с ростом данных, а конкретно с импортом блога.

Следовательно, решение самой проблемы заключалось в замене метода isAncestor нашей собственной реализацией + применении OutputCache к нашему элементу управления.



выводы

— Высокая загрузка ЦП — это не только рекурсия или большая нагрузка, но и сбор мусора; — Создание объектов должно быть продуманным и соответствовать архитектуре приложения; — Вывод кэша — всегда и везде; — Все, что не видно при обычном тестировании, появится при нагрузочном тестировании! И обратите внимание: На момент написания статьи NewRelic помог мне найти не источник High CPU, а счетчик производительности.

% времени в GC легко указал на источник проблемы.

Если на графике пики ЦП увеличиваются в соответствии с пиками графика % времени в GC и смысл % времени в GC выше линии 20% => Высокая загрузка ЦП из-за сборки мусора.

Спасибо за внимание.

Надеюсь, это было интересно.

Теги: #.

NET #ASP.NET #umbraco #производительность #утечки памяти #dotMemory #разработка веб-сайтов #.

NET #ASP #ASP

Вместе с данным постом часто просматривают: