Как организовано управление памятью в C#?

«Как организовано управление памятью в C#?» — вопрос из категории Управление памятью, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Управление памятью в C# основано на разделении на стек (stack) и управляемую кучу (managed heap), а также автоматической сборке мусора (Garbage Collection, GC).

1. Стек (Stack)

  • Хранит: Локальные переменные значимых типов (Value Types) — int, bool, struct, а также ссылки (указатели) на объекты в куче.
  • Работа: Выделение и освобождение памяти происходит быстро и детерминировано, следуя принципу LIFO (Last-In-First-Out). Память освобождается сразу при выходе из области видимости (метода, блока кода).
  • Пример:
    void MyMethod() {
        int number = 42; // 'number' и его значение живут в стеке
        Person personRef; // Ссылка (4/8 байт) живет в стеке, сам объект — нет.
    } // При выходе из метода память для 'number' и 'personRef' мгновенно освобождается.

2. Управляемая куча (Managed Heap)

  • Хранит: Объекты ссылочных типов (Reference Types) — экземпляры class, string, массивы.
  • Работа: Память выделяется при создании объекта (new). Освобождением занимается сборщик мусора (GC). Доступ к объекту осуществляется по ссылке из стека.
  • Пример:
    void MyMethod() {
        // Ссылка 'person' живет в стеке.
        // Сам объект Person (с полями Name, Age) создается в управляемой куче.
        Person person = new Person { Name = "Alice", Age = 30 };
    }

3. Сборка мусора (Garbage Collection) GC автоматически освобождает память в куче, когда объекты становятся недостижимыми (на них больше нет активных ссылок).

  • Поколения (Generations): Куча разделена на три поколения (Gen 0, Gen 1, Gen 2) для оптимизации.
    • Gen 0: Новые, недолгоживущие объекты. Сборка происходит часто и быстро.
    • Gen 1: Объекты, пережившие одну сборку Gen 0.
    • Gen 2: Долгоживущие объекты (например, статические данные, кэши). Сборка происходит реже, но она самая дорогая.
  • Большой объектный куч (LOH): Объекты больше ~85 КБ размещаются в LOH, который собирается только во время сборки Gen 2.

4. Практические следствия для разработчика

  • Утечки памяти в .NET возникают, когда на объекты неявно сохраняются ссылки (через события, статические коллекции, кэши).
  • Для работы с неуправляемыми ресурсами (файлы, сокеты) реализуйте интерфейс IDisposable и используйте конструкцию using.
    using (var fileStream = new FileStream("file.txt", FileMode.Open))
    {
        // работа с потоком
    } // Dispose() вызовется автоматически, даже при исключении
  • Для высокопроизводительных сценариев используйте Span<T> и Memory<T> для работы с памятью на стеке или в пулах, минимизируя аллокации в куче.