---
title: Управление памятью
seo:
  title: "Управление памятью — С# Developer"
  description: "Тема «Управление памятью» для собеседования С# Developer. Что такое куча и стек? Различия, принцип работы. Как работает сборщик мусора?"
---

[Все темы С# Developer](/s-developer)

## <strong>Что такое куча и стек? Различия, принцип работы</strong> [#q-14bee738d69b81bfaa97f7e7d4fc3ac0]

Куча (<code>Heap</code>) - это область динамической памяти, используемая для хранения объектов и данных, которые создаются и освобождаются во время выполнения программы.

<strong>Принцип работы&#58;</strong>

- Управление памятью осуществляется автоматически через сборщик мусора (Garbage Collector) или вручную через операторы выделения и освобождения памяти (<code>new</code>, <code>delete</code> в C++, <code>malloc</code>, <code>free</code> в C).

<strong>Особенности&#58;</strong>

- Гибкость в выделении памяти, так как размер памяти может изменяться во время выполнения.

- Более медленный доступ по сравнению со стеком из-за необходимости поиска свободного места и управления фрагментацией.

Стек (<code>Stack</code>) - это область памяти, используемая для хранения локальных переменных, вызовов функций и управления потоком выполнения программы.

<strong>Принцип работы&#58;</strong>

- Работает по принципу LIFO (Last In, First Out), где последняя выделенная память освобождается первой.

- Управление памятью осуществляется автоматически с помощью указателя стека.

<strong>Особенности&#58;</strong>

- Быстрый доступ, так как выделение и освобождение памяти происходит по фиксированному порядку.

- Ограниченный размер, определяемый при запуске программы.

- Стек содержит кадры вызовов и часть локальных данных. JIT может хранить значения в регистрах, а захваченные локальные переменные — в объектах кучи; тип значения сам по себе не определяет размещение.

#### Основные различия&#58;

{/* prettier-ignore */}
- <strong>Управление памятью&#58;</strong>

    - <strong>Куча&#58;</strong> Динамическое, управляемое сборщиком мусора или вручную.

    - <strong>Стек&#58;</strong> Автоматическое, управляемое указателем стека.

- <strong>Скорость доступа&#58;</strong>

    - <strong>Куча&#58;</strong> Медленнее из-за динамического выделения и освобождения.

    - <strong>Стек&#58;</strong> Быстрее из-за статического порядка выделения и освобождения.

- <strong>Размер&#58;</strong>

    - <strong>Куча&#58;</strong> Может быть большой и изменяться во время выполнения.

    - <strong>Стек&#58;</strong> Обычно ограничен и задан при запуске программы.

- <strong>Использование&#58;</strong>

    - <strong>Куча&#58;</strong> Для объектов и данных, которые живут дольше одного вызова функции или имеют неопределенный размер.

    - <strong>Стек&#58;</strong> Для локальных переменных и параметров функций.

:::note[Ссылки для изучения]

1. [Принципы программирования. Стек и куча](https://otus.ru/nest/post/605/)
   :::

---

## <strong>Как работает сборщик мусора?</strong> [#q-14bee738d69b819c9575c93908db4903]

Сборщик мусора (<code>Garbage Collector</code>) - это механизм автоматического управления памятью, который освобождает память, занятую объектами, больше не используемыми программой.

<strong>Принцип работы&#58;</strong>

{/* prettier-ignore */}
1. <strong>Обнаружение неиспользуемых объектов&#58;</strong>

    - <strong>Описание&#58;</strong> Определяет объекты, которые больше не доступны из кода программы, и помечает их для удаления.

    - Стратегия .NET&#58; трассирующий GC определяет достижимость от корней и использует поколения. Он не основан на подсчете ссылок.

1. <strong>Освобождение памяти&#58;</strong>

    - <strong>Описание&#58;</strong> Удаляет помеченные объекты и возвращает занимаемую ими память в пул свободной памяти.

    - <strong>Процесс&#58;</strong> Включает в себя компактацию памяти, чтобы уменьшить фрагментацию.

1. <strong>Сборка поколений&#58;</strong>

    - <strong>Описание&#58;</strong> Объекты разделяются на поколения (молодые, старые) для оптимизации производительности.

    - <strong>Поколения&#58;</strong> Молодые объекты часто собираются быстрее, старые объекты реже.

1. <strong>Фазы работы&#58;</strong>

    - <strong>Маркировка (Mark)&#58;</strong> Поиск и пометка всех объектов, к которым есть доступ.

    - <strong>Очистка (Sweep)&#58;</strong> Удаление объектов, которые не были помечены.

    - <strong>Компактация (Compaction)&#58;</strong> Перемещение оставшихся объектов, чтобы освободить непрерывные блоки памяти.

<strong>Преимущества</strong>
<strong>&#58;</strong>

- <strong>Автоматизация&#58;</strong> Упрощает управление памятью для
  разработчика.

- <strong>Безопасность&#58;</strong> Снижает вероятность утечек памяти и ошибок
  доступа к памяти.

<strong>Недостатки</strong>
<strong>&#58;</strong>

- <strong>Производительность&#58;</strong> Может временно замедлить работу
  приложения во время выполнения сборки мусора.

- <strong>Непредсказуемость&#58;</strong> Время запуска и продолжительность
  работы сборщика мусора могут быть непредсказуемыми.

#### Пример в .NET&#58;

```csharp
// Создание объектов
var obj1 = new MyClass();
var obj2 = new MyClass();

// Удаление ссылок на объекты
obj1 = null;
obj2 = null;

// В какой-то момент сборщик мусора освободит память, занятую obj1 и obj2
```

Сборщик мусора управляет памятью, позволяя разработчикам сосредоточиться на логике приложения, а не на ручном управлении памятью.

:::note[Ссылки для изучения]

1. [Сборка мусора в .NET](https://learn.microsoft.com/ru-ru/dotnet/standard/garbage-collection/)
   :::

---

## Зачем нужны using и IDisposable при наличии GC? [#q-14bee738d69b81c89051f358ca8935a5]

<strong>Зачем нужно зарезервированное слово</strong> <code>using</code>
<strong>в C#&#58;</strong>

- <strong>Управление ресурсами&#58;</strong> <code>using</code> используется для
  автоматического освобождения ресурсов, как управляемых (managed), так и
  неуправляемых (unmanaged), по завершении их использования.

- <strong>Освобождение ресурсов&#58;</strong> Обеспечивает вызов метода
  <code>Dispose</code> для объектов, реализующих интерфейс
  <code>IDisposable</code>, что позволяет своевременно освобождать ресурсы.

#### Пример использования <code>using</code>&#58;

```csharp
using (var resource = new Resource())
{
    // Использование ресурса
}
// Ресурс автоматически освобождается после блока using
```

#### Disposable-паттерн&#58;

<strong>Описание&#58;</strong>

- <strong>Интерфейс</strong> <code>IDisposable</code>
  <strong>&#58;</strong> Определяет метод <code>Dispose</code>, который должен
  быть реализован для освобождения ресурсов.

- <strong>Реализация&#58;</strong> Классы, использующие неуправляемые ресурсы,
  реализуют этот интерфейс для освобождения памяти и других ресурсов.

#### Пример реализации <code>IDisposable</code>&#58;

```csharp
public class Resource : IDisposable
{
    private bool disposed = false;

    // Реализация метода Dispose
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Освобождение управляемых ресурсов
            }
            // Освобождение неуправляемых ресурсов
            disposed = true;
        }
    }

    ~Resource()
    {
        Dispose(false);
    }
}
```

#### Почему так важно&#58;

- <strong>Управляемые ресурсы&#58;</strong> .NET автоматически управляет
  памятью, но управляемые ресурсы могут включать в себя объекты, которые сами
  управляют неуправляемыми ресурсами (например, файлы, сетевые соединения).

- <strong>Неуправляемые ресурсы&#58;</strong> Это ресурсы, которые не
  контролируются средой выполнения .NET (например, хендлы файлов, соединения с
  базой данных). Их необходимо освобождать вручную, иначе они могут привести к
  утечкам памяти и другим проблемам.

#### Итог&#58;

- <code>using</code>
  <strong>&#58;</strong> Обеспечивает удобный синтаксис для гарантированного
  освобождения ресурсов.

- <code>Disposable</code>
  <strong>-паттерн&#58;</strong> Позволяет правильно управлять и освобождать как
  управляемые, так и неуправляемые ресурсы, предотвращая утечки и повышая
  надежность приложения.

:::note[Ссылки для изучения]

1. [Using block in C#](https://stackoverflow.com/questions/212198/what-is-the-c-sharp-using-block-and-why-should-i-use-it)
   :::

---

## <strong>Какие особенности работы с</strong> <code>Large Object Heap</code><strong>?</strong> [#q-14bee738d69b81d2bb3ef9cadee31a7e]

<strong>Large Object Heap</strong> (<code>LOH</code>) – это специальная область
памяти в .NET, предназначенная для хранения крупных объектов размером 85 000
байт и больше.

<strong>Размещение объектов&#58;</strong>
Крупные объекты, такие как большие массивы или строки, размещаются в LOH. Это
делается для оптимизации производительности, так как такие объекты требуют
значительного объема непрерывной памяти.

LOH собирается вместе с поколением 2. По умолчанию крупные объекты обычно не перемещаются, чтобы избежать дорогого копирования; это может оставлять свободные промежутки.

<strong>Сборка мусора&#58;</strong>
Сборка мусора для LOH происходит реже, чем для SOH, обычно при полной сборке
мусора (Generation 2). Это может привести к фрагментации памяти, так как объекты
в LOH не перемещаются.

LOH может фрагментироваться. Начиная с .NET Framework 4.5.1 можно запросить его компактацию через GCSettings.LargeObjectHeapCompactionMode; поэтому рассчитывать на неизменный адрес объекта нельзя.

<strong>Оптимизация производительности&#58;</strong>
Для оптимизации производительности рекомендуется минимизировать количество
крупных объектов и избегать их частого создания и удаления. Это поможет
уменьшить нагрузку на LOH и предотвратить фрагментацию памяти.

<strong>Пример включения компактной сборки мусора для LOH&#58;</strong>

```csharp
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(2, GCCollectionMode.Forced);
```

:::note[Ссылки для изучения]

1. [Куча больших объектов (LOH)](https://learn.microsoft.com/ru-ru/dotnet/standard/garbage-collection/large-object-heap)
   :::
