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 (синий цвет), настолько большие, что казалось, что сборка мусора не может справиться. Стратегия компенсации большого объема памяти привела к тому, что сборка мусора колебалась между увеличением пространства памяти (добавлением большего количества памяти для использования приложением) и его очисткой. Во время циклов очистки приложение не отвечает.

Сначала мы были озадачены. Разве цель сборщика мусора не в том, чтобы поддерживать память в порядке? Две статьи были инструментальны в нашем понимании того, как работает сборка мусора в .Net: статья Mark Vincze Troubleshooting high memory usage with ASP.Net Core on Kubernetes и Fundamentals of Garbage Collection от Microsoft. Обе статьи — отличное чтение и прояснили использование памяти в Ed-Pro.

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

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

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

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

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

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

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

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

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

Автор: Chuck Conway — инженер AI с почти 30-летним опытом разработки программного обеспечения. Он создает практические системы AI — конвейеры контента, агенты инфраструктуры и инструменты, которые решают реальные проблемы — и делится тем, что он узнает на этом пути. Свяжитесь с ним в социальных сетях: X (@chuckconway) или посетите его на YouTube и на SubStack.

↑ Вернуться в начало

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