Збирач сміття (Garbage Collector, GC) є ключовим компонентом платформи .NET, що забезпечує автоматичне керування пам'яттю та запобігає витокам. У версії .NET 9, випущеній у листопаді 2024 року, відбулися значні покращення GC, спрямовані на підвищення ефективності пам'яті, зниження латентності та адаптацію до динамічних навантажень. Основними інноваціями є активація за замовчуванням механізму DATAS (Dynamic Adaptation to Application Sizes), адаптивний Server GC та оптимізації, пов'язані з JIT-компіляцією. Ця оглядова стаття аналізує ці зміни, їх механізми, вплив на продуктивність та рекомендації щодо використання. На основі офіційної документації Microsoft та бенчмарків, демонструється зниження споживання пам'яті на 93% та зростання throughput на 15% у типових сценаріях. Стаття корисна для розробників, які працюють з високонавантаженими додатками, особливо в хмарних та контейнеризованих середовищах.
Ключові слова: .NET 9, Garbage Collector, DATAS, Server GC, адаптивне керування пам'яттю, продуктивність, латентність.
Історія розвитку збирача сміття в .NET сягає часів .NET Framework 1.0, де GC базувався на поколінному підході (generational GC) з двома основними режимами: Workstation GC (для клієнтських додатків) та Server GC (для серверних, з паралельними потоками). З переходом на .NET Core та .NET 5+, акцент змістився на кросплатформенність, оптимізацію для хмар (наприклад, Azure) та контейнери (Docker, Kubernetes). У .NET 8 з'явилися перші елементи адаптивності, але .NET 9 робить крок уперед, роблячи GC "розумнішим" – здатним динамічно реагувати на розмір додатка та навантаження, а не лише на апаратні ресурси.
Чому це важливо? У сучасних додатках, таких як мікросервіси чи веб-API, флуктуації навантаження призводять до неефективного використання пам'яті: фіксований heap у Server GC може "переїдати" ресурси в низьконавантажених періодах, а Workstation GC – не справлятися з багатоядерними системами. За даними TechEmpower, оптимізації .NET 9 знижують memory footprint на 93% та підвищують requests per second (RPS) на 15%. Ця стаття оглядає ключові фішки: DATAS, адаптивний Server GC, покращення латентності та інтеграцію з JIT, з прикладами та бенчмарками.
Перш ніж перейти до новинок, коротко нагадаємо базову модель. GC у .NET поділяє об'єкти на покоління (Gen0 – короткоживучі, Gen1 – проміжні, Gen2 – довгоживучі, LOH – великі об'єкти). Колекція починається з Gen0, і якщо пам'ять не звільняється, переходить до старших поколінь. Server GC використовує окремі heap на ядро для паралелізму, що ідеально для серверів, але фіксований розмір (зазвичай 4x від робочого набору) призводить до надмірного споживання в контейнерах.
У .NET 9 фокус на адаптивності: GC моніторить long-lived data (довгоживучі об'єкти) та динамічно налаштовує heap, зменшуючи паузи та алокації. Це інтегровано з JIT-оптимізаціями, які зменшують кількість об'єктів, що створюються (наприклад, елімінація boxing).
DATAS (Dynamic Adaptation to Application Sizes) – це ключова фішка .NET 9, введена як опція в .NET 8 і активована за замовчуванням для Server GC у .NET 9. Вона динамічно налаштовує розмір heap пропорційно до обсягу long-lived data, а не фіксовано за апаратними ресурсами (CPU/пам'ять). Алгоритм моніторить:
- Розмір Gen2 (довгоживучі об'єкти).
- Частоту алокацій та колекцій.
- Навантаження (throughput vs latency).
Результат: heap росте поступово, а не стрибками, що особливо корисно в контейнерах, де ресурси обмежені (наприклад, 256 MB у Kubernetes pod). У класичному Server GC heap міг сягати гігабайт навіть при низькому навантаженні; DATAS тримає його в 1.5–2x від реального робочого набору.
Тести показують:
-
Зниження memory usage: У сценарії з флуктуаційним навантаженням (k6 load testing) .NET 9 з DATAS демонструє поступовий ріст LOH (Large Object Heap), проти стрибків у .NET 8. Загальне споживання – на 50–70% менше в низьконавантажених періодах.
-
GC-паузи: Більш часті (Gen0 кожні 10–20 сек), але коротші (1–5 мс vs 10–50 мс). Розподіл пауз рівномірніший, без "спайків".
-
Throughput: +10–15% RPS у TechEmpower для веб-сервісів.
Приклад конфігурації (якщо потрібно вимкнути для максимального throughput):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<GarbageCollectionAdaptationMode>0</GarbageCollectionAdaptationMode>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
</Project>У цьому режимі DATAS ігнорується, повертаючись до legacy Server GC.
Уявіть ASP.NET Core API з динамічним навантаженням. Без DATAS: при піках heap розростається до 1 GB, викликаючи OOM (OutOfMemory). З DATAS: heap адаптується до 200–400 MB, з регулярними Gen0. Тест з Minikube показує: .NET 9 – 30% менше подій GC, але ефективніші.
Server GC у .NET 9 перероблено для адаптації до вимог додатка, а не ресурсів середовища. Раніше фіксований "heap-per-core" (окремий сегмент на ядро) призводив до надмірного використання в high-core системах з малим робочим набором (наприклад, 64 ядра, але 100 MB data). Тепер GC динамічно групує сегменти, зменшуючи contention.
Ключові покращення:
-
Паралельна компакція: На Linux – vxsort у паралельному режимі, що скорочує паузи на 20–30% під час Gen2 колекцій.
-
Зниження алокацій через JIT: Елімінація boxing у
ArgumentNullException.ThrowIfNull(з 2400 B до 0),Nullable<T>checks (allocation-free) таDelegate.EnumerateInvocationList(48 B → 0). Це зменшує тиск на GC на 15–25%. -
Конкурентність: Покращена thread scheduling для багатоядерних машин, з меншим contention у concurrent collections.
| Сценарій | .NET 8 (Server GC) | .NET 9 (Adaptive) | Покращення |
|---|---|---|---|
| TechEmpower RPS | 1,000,000 req/s | 1,150,000 req/s | +15% |
| Memory footprint | 1.5 GB | 100 MB | -93% |
| Gen2 pause time | 50 ms | 20 ms | -60% |
| Allocation rate (low load) | 100 MB/s | 40 MB/s | -60% |
Джерело: Офіційні бенчмарки Microsoft. У контейнерах (Docker) – ще кращі результати: LOH росте лінійно, без стрибків.
Legacy Server GC можна повернути:
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
<GCHeapHardLimit>500MB</GCHeapHardLimit> <!-- Обмеження для контейнерів -->
</PropertyGroup>Рекомендація: Використовуйте DATAS за замовчуванням; вимкніть лише для стабільно високого навантаження (e.g., batch processing).
Gen2 – "ахіллесова п'ята" для довгоживучих додатків. У .NET 9:
-
Enhanced Compaction: Автоматична компакція LOH для уникнення фрагментації, з parallel sorting.
-
Low-Latency Mode: Розширено для Server GC, з динамічним балансом між throughput та паузами (конфіг:
GCLatencyMode.LowLatency).
Багато фішок GC – результат JIT-оптимізацій:
-
Loop Optimizations: Downward counting loops (+12% швидкість), bounds check elision (до 76% у string.Equals).
-
SIMD/Vectorization: AVX512 для zeroing (67% швидше DoubleToUlong), що зменшує алокації.
-
Reflection Speedups:
FieldInfo.GetValue– 90% швидше, знижуючи GC тиск у DI (Dependency Injection).
Приклад коду з оптимізацією (використання SearchValues<T> для пошуку, замість loops):
using System.Buffers.Text; // .NET 9 enhancement
var searchValues = SearchValues.Create(new[] { "foo", "bar" });
if (searchValues.IndexOf("hello world") >= 0) { /* ... */ }
// 100x швидше, менше алокаційНові фішки GC у .NET 9 – це еволюція від статичного до адаптивного керування пам'яттю, з DATAS як "серцем" змін. Вони роблять платформу ідеальною для сучасних сценаріїв: контейнери, мікросервіси, AI-додатки з флуктуаціями. Переваги – у зниженні memory на 93%, скороченні пауз та +15% throughput – перевищують мінімальні витрати (e.g., modest throughput drop у legacy режимах). Рекомендації: оновлюйте до .NET 9, тестуйте з BenchmarkDotNet, моніторьте через OpenTelemetry. Майбутнє – у подальшій інтеграції з AI для передбачення навантажень.
Подальші дослідження: Вплив на NativeAOT та ARM-архітектури.
-
Microsoft. (2024). What's new in .NET 9 runtime.
-
Microsoft. (2024). Performance Improvements in .NET 9.
-
Vensas. (2025). DATAS and Server Garbage Collection in .NET 9.
-
Microsoft. (2024). Announcing .NET 9.
-
C# Corner. (2024). Understanding the Working of Garbage Collector in .NET 9.