Skip to content

Посты

Типы сборки мусора в .Net Core

2 сентября 2019 г. • 4 мин чтения

Типы сборки мусора в .Net Core

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

В Калифорнии существуют обширные требования к финансовой отчетности для местных образовательных агентств (LEA), LEA может быть округом, районом, чартерной школой или отдельной школой. Большинство LEA создают свои собственные финансовые отчеты, которые обычно основаны на Excel, неудивительно, что каждый отчет отличается. Для решения этой проблемы Совет по образованию Калифорнии заказал программное обеспечение для генерации финансовых отчетов.

Я был частью команды разработчиков.

Моей первой остановкой были журналы тестирования, журналы Ed-Pro указывали на высокое использование памяти, возможно, была утечка памяти? Инженер заметил, что вычисления Ed-Pro использовали большое количество кратковременной памяти. Если память не очищалась быстро, это могло выглядеть как утечка памяти.

Ed-Pro построен на основе .Net Core, многоплатформенного фреймворка Microsoft. В .Net Core память разделена на три категории: кратковременная (Gen0), средней продолжительности (Gen1) и долговременная (Gen2). Gen0 предназначена для кратковременных данных, которые быстро выходят из области видимости, Gen1 - для памяти средней продолжительности, которая существует немного дольше, но в конечном итоге также выходит из области видимости, а Gen2 - это долговременная память, которая может существовать в течение всего времени работы приложения. Память Gen0 постоянно освобождается, Gen1 освобождается реже, чем Gen0, а Gen2 освобождается еще реже, чем Gen1.

Единственным надежным способом понять использование памяти Ed-Pro было профилирование, ниже представлен скриншот с использованием dotMemory от JetBrains.

Как и предполагалось, мы обнаружили большие объемы памяти Gen0 (синий цвет), настолько большие, что казалось, будто сборка мусора не успевает. Стратегия компенсации большого объема памяти заставляла сборку мусора колебаться между увеличением пространства памяти (добавлением большего количества памяти для использования приложением) и ее очисткой. Во время циклов очистки приложение не отвечает.

Сначала мы были в тупике, разве не в том заключается цель GC, чтобы поддерживать память в порядке? Две статьи были ключевыми для нашего понимания того, как работает сборка мусора в .Net: статья Марка Винце Устранение неполадок высокого использования памяти с ASP.Net Core на Kubernetes и Основы сборки мусора от Microsoft. Обе статьи являются отличным чтением и внесли ясность в использование памяти в Ed-Pro.

Вот краткое изложение того, что мы узнали: в .Net существует два типа сборки мусора: серверная сборка мусора и рабочая станция сборки мусора.

Серверная сборка мусора делает несколько предположений: во-первых, доступно достаточно памяти, и во-вторых, процессоры многоядерные и быстрые. Оба условия могут быть истинными, но мы живем в мире виртуальных машин и Docker, где более вероятно, что оба предположения ложны.

Серверная сборка мусора позволяет памяти накапливаться, в какой-то момент она делает одно из двух: либо увеличивает пространство памяти, позволяя памяти расти, либо освобождает осиротевшую память. Когда она выбирает освобождение памяти, сборка мусора запускает процесс в высокоприоритетном потоке. Высокоприоритетный поток имеет более высокий приоритет, чем приложение; если машина быстрая, очистка не должна быть заметна. Однако, если это не так, это приведет к остановке приложения до завершения очистки.

Сборка мусора рабочей станции работает по-другому. Она непрерывно работает, освобождая память в потоке с тем же приоритетом, что и приложение. Это означает, что она также конкурирует за ресурсы с приложением, что может вызвать замедление приложения. Преимущество заключается в том, что использование памяти приложением может оставаться довольно низким, особенно когда оно использует большие объемы Gen0.

По умолчанию, если .Net Core обнаруживает сервер, он запускает тип серверной сборки мусора, что и было в случае с нашим приложением. Чтобы запустить тип сборки мусора рабочей станции, добавьте следующий фрагмент в файл проекта:

  <PropertyGroup> 
    <ServerGarbageCollection>false</ServerGarbageCollection>
  </PropertyGroup>

Мы внесли это изменение конфигурации в Ed-Pro, используя dotMemory, мы профилировали память Ed-Pro с включенной сборкой мусора рабочей станции и загрузили те же экраны, что и в предыдущем тесте. Вот результаты:

Использование памяти значительно снизилось. Выделения Gen0 практически отсутствуют. Помимо различий в графике, использование памяти серверной сборки мусора достигло 1 гигабайта, в то время как сборка мусора рабочей станции достигла примерно 200 мегабайт.

Каждое приложение отличается. Наше приложение использовало тонну временных данных и, таким образом, использует тонну памяти Gen0. Ваше приложение может использовать память более длительного срока жизни, такую как Gen1 или Gen2, в этом случае серверная сборка мусора имеет большой смысл. Мой совет - профилировать вашу память в различных условиях, чтобы получить представление о том, как используется память, а затем решить, какой режим лучше всего подходит для вашего приложения.

Автор: Чак Конвей специализируется на разработке программного обеспечения и генеративном ИИ. Свяжитесь с ним в социальных сетях: X (@chuckconway) или посетите его на YouTube.

↑ Наверх

Вам также может понравиться