Ответ вслух Senior GC, IDisposable и производительность обновлено

Как снизить аллокации в горячем коде

Вопрос

Как вы снижаете нагрузку на GC в горячем пути сервиса?

Попробуйте сами

Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.

Показать ответ и разбор

Коротко

Начинаю с измерений: BenchmarkDotNet с MemoryDiagnoser показывает байты на операцию, а dotnet-counters в проде частоту сборок и размер поколений. Оптимизирую только то, что реально горячее.

Что должно прозвучать в ответе

  • Сначала измерить: BenchmarkDotNet с MemoryDiagnoser, dotnet-counters, профилировщик

  • Span и stackalloc для временных буферов, парсинг без Substring

  • ArrayPool и ObjectPool для переиспользования

  • Структуры вместо классов для мелких значений, без упаковки

  • Избегать LINQ и замыканий в самых горячих циклах

  • Ёмкость коллекций и StringBuilder заранее

Что могут спросить следом

  • Когда stackalloc опасен?
  • Чем плох большой Gen2 и как его заметить в метриках?

Разбор

Начинаю с измерений: BenchmarkDotNet с MemoryDiagnoser показывает байты на операцию, а dotnet-counters в проде частоту сборок и размер поколений. Оптимизирую только то, что реально горячее.

Дальше типичные приёмы. Временные буферы через Span<T>: небольшие на стеке через stackalloc, большие из ArrayPool<T>.Shared с обязательным Return. Парсинг через ReadOnlySpan<char> вместо Substring и Split. Для дорогих объектов ObjectPool<T>, например для StringBuilder.

Мелкие значения делаю readonly struct и слежу, чтобы они не упаковывались через интерфейсы. В самых горячих циклах убираю LINQ и лямбды с захватом. Коллекциям и StringBuilder задаю ёмкость заранее, чтобы не было переаллокаций. И отдельно слежу за LOH: большие временные массивы, например при чтении тел запросов, это прямой путь к полным сборкам.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.