.NET Memory Management Mastery | Quizlet

0.0(0)
Studied by 0 people
call kaiCall Kai
learnLearn
examPractice Test
spaced repetitionSpaced Repetition
heart puzzleMatch
flashcardsFlashcards
GameKnowt Play
Card Sorting

1/31

encourage image

There's no tags or description

Looks like no tags are added yet.

Last updated 2:46 PM on 6/17/26
Name
Mastery
Learn
Test
Matching
Spaced
Call with Kai

No analytics yet

Send a link to your students to track their progress

32 Terms

1
New cards

Когда триггерится сборка мусора?

Основной сценарий: при попытке выделить память в Gen 0, когда там нет свободного места

Другие триггеры:

- При выделении в LOH, если превышен порог

- При нехватке памяти в системе (memory pressure)

- По таймеру в фоновом GC

- Явный вызов GC.Collect()

Пример кода для явного вызова:

GC.Collect(); // собирает все поколения

GC.Collect(0); // только Gen 0

GC.Collect(2, GCCollectionMode.Forced); // принудительно Gen 2

2
New cards

Какие есть фазы сборки мусора? В каком порядке они происходят?

Mark — пометка живых объектов

Sweep — удаление мертвых объектов

Compact — уплотнение кучи (перемещение объектов)

3
New cards

Что происходит во время фазы Mark? Как GC помечает объекты?

GC проходит граф объектов от GC Roots, помечая каждый достижимый объект специальным битом

Как это работает:

- У каждого объекта в куче есть заголовок (object header)

- В заголовке есть специальный бит для пометки GC

- GC устанавливает этот бит в 1 для живых объектов

- Если объект уже помечен — повторно не обрабатывается (избегание циклов)

Заголовок объекта содержит:

- Sync block index (для lock и других целей)

- Method table pointer (информация о типе)

- Флаги, включая бит пометки GC

4
New cards

Что происходит во время фазы Sweep?

GC проходит по тем поколениям, которые мы собираем и освобождает память от объектов, которые НЕ помечены как живые. В куче остаются только живые объекты, но между ними образуются дыры

5
New cards

Что происходит во время фазы Compact? Что со ссылками?

GC перемещает все живые объекты к началу кучи, убирая фрагментацию. GC автоматически обновляет ВСЕ ссылки на перемещенные объекты. Мы никогда не получим битую ссылку после GC

6
New cards

Что такое GC Roots? Что является GC Roots?

Точки входа для поиска живых объектов:

- Локальные переменные и параметры методов в стеке

- Статические поля классов

- Регистры процессора

- Pinned объекты

- Handles в P/Invoke сценариях

- Объекты в финализационной очереди

- Объекты, на которые ссылаются из finalizer-reachable очереди

7
New cards

Какие объекты считаются мусором?

Недостижимые объекты — те, до которых невозможно добраться ни по одному пути от любого GC Root

Аналогия: представь сундук с объектами, где некоторые объекты связаны между собой лесками. Из сундука торчат концы некоторых лесок — это GC Roots. Если потянуть за любую торчащую леску и по цепочке лесок можно достать объект (напрямую или через другие объекты), то он достижимый. Объекты, которые никак не связаны с торчащими лесками — мусор

Объект должен быть достижим от GC Roots — точек, которые гарантированно живые (локальные переменные активных методов, статические поля и т.д.)

8
New cards

Зачем нужны поколения в GC?

Оптимизация на основе гипотезы поколений: молодые объекты умирают быстрее старых. 85% объектов убирают в поколении 0.

Сборка Gen 0 происходит быстро (мало объектов). Долгоживущие объекты в Gen 2 проверяются реже

9
New cards

При сборке поколения X какие поколения сканируются?

Gen 0: сканируется только Gen 0

Gen 1: сканируются Gen 0 + Gen 1

Gen 2: сканируются Gen 0 + Gen 1 + Gen 2 (full GC)

10
New cards

Если на объект из Gen 0 ссылается только объект из Gen 2, соберет ли его GC?

Нет. При сборке в Gen 0 и Gen 1 проверяются ссылки из старших поколений через card table

11
New cards

Что такое LOH и какие объекты туда попадают?

Large Object Heap — отдельная куча для объектов размером ≥85000 байт (размер можно настроить)

Что попадает:

- Большие массивы

- Крупные строки

- Большие объекты

Особенности:

- Сразу считается Gen 2

- Долго не было дефрагментации (сейчас есть опционально)

Пример:

byte[] bigArray = new byte[100_000]; // попадет в LOH

string longString = new string('x', 50_000); // тоже в LOH

12
New cards

Почему большие объекты не хранятся в обычной куче?

Перемещение больших объектов во время Compact фазы слишком дорого по времени

Компромисс: Скорость vs фрагментация

13
New cards

Есть ли Compact у LOH?

.NET Framework: НЕТ (только Mark & Sweep)

.NET Core/.NET 5+: ДА, но по требованию через настройки

Включение дефрагментации:

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;

GC.Collect(); // следующая полная сборка выполнит дефрагментацию LOH

14
New cards

Что такое "Stop the World"?

Приостановка всех потоков приложения во время критических фаз сборки мусора для обеспечения целостности кучи

15
New cards

В чем разница между Server и Workstation GC?

Workstation GC:

- Одна общая куча для всех потоков

- Оптимизирован для минимальных пауз (UI отзывчивость)

- Concurrent GC по умолчанию

- Меньше потребляет памяти

- Сборка на том же потоке, что выделял память

Server GC:

- Отдельный сегмент кучи для каждого логического процессора

- Объекты могут ссылаться между сегментами

- Выделение памяти происходит в "своем" сегменте потока

- Параллельная сборка несколькими потоками

- Оптимизирован для пропускной способности

- Background GC для Gen 2

- Больше потребляет памяти

Важно: это всё одна управляемая куча, просто разделенная на сегменты для параллельной работы. Объект из сегмента CPU 0 может свободно ссылаться на объект из сегмента CPU 1

Настройка:

true

16
New cards

Зачем нужен Pinned Object Heap (POH)?

Специальная куча для закрепленных (pinned) объектов, которые нельзя перемещать в памяти

Что такое пиннинг: когда мы передаем объект в неуправляемый код (например, Windows API или C++ библиотеку), мы даем ему адрес объекта в памяти. GC не может переместить такой объект, потому что не сможет обновить этот адрес в неуправляемом коде

Аналогия: это как дать кому-то карту с местом клада. Если мы переместим клад, но не можем обновить карту — человек придет на старое место и ничего не найдет

До POH: pinned объекты создавали дыры в обычной куче (фрагментация)

С POH: такие объекты сразу попадают в специальную кучу

Пример использования:

byte[] buffer = GC.AllocateArray(1024, pinned: true);

// buffer в POH, можно безопасно передавать в native код

17
New cards

Зачем нужен финализатор? Какой код туда помещать?

Для освобождения неуправляемых ресурсов (файловые дескрипторы, нативная память, хэндлы Windows)

Пример кода с финализатором:

class FileWrapper

{

private IntPtr fileHandle;

~FileWrapper()

{

if (fileHandle != IntPtr.Zero)

{

NativeMethods.CloseHandle(fileHandle);

}

}

}

18
New cards

Когда вызывается финализатор?

После того, как объект определен как мусор, но перед его удалением

1. Объект становится недостижимым

2. GC помещает объект в очередь финализации

3. Отдельный поток выполняет финализаторы

4. При следующей сборке объект удаляется

Важно: Финализатор продлевает жизнь объекта минимум на одну сборку!

19
New cards

На каком потоке и с какими гарантиями выполняется финализатор?

Специальный отдельный поток финализации

Гарантии:

- НЕТ гарантий порядка выполнения

- НЕТ гарантий времени выполнения

- НЕТ гарантий выполнения при завершении приложения

- Если финализатор завис, он блокирует выполнение всех остальных

- Если упал с исключением, исключение игнорируется

20
New cards

Как убрать объект из очереди финализации?

GC.SuppressFinalize(this) — обычно вызывается в методе Dispose()

public void Dispose()

{

// Освобождаем ресурсы

CleanupResources();

// Говорим GC не вызывать финализатор

GC.SuppressFinalize(this);

}

21
New cards

Как влияет наличие финализатора на срок жизни объекта?

Финализатор увеличивает время жизни объекта:

1. Объект переживает первую сборку мусора

2. Помещается в очередь финализации

3. Переводится в следующее поколение

4. Удаляется только при следующей сборке этого поколения

Пример влияния:

- Объект без финализатора в Gen 0: удален при первой же сборке

- Объект с финализатором в Gen 0: переживет сборку, попадет в Gen 1, удален не раньше сборки Gen 1

22
New cards

Что такое управляемые и неуправляемые ресурсы (managed и unmanaged)? Управляемые кем?

Управляемые .NET Runtime — находятся в памяти, про которую знает GC

Управляемые (99.999% того, с чем мы работаем):

- Все обычные объекты C#

- Массивы, строки, коллекции

- Объекты-обертки (FileStream, SqlConnection)

Неуправляемые (прямые ресурсы ОС):

- Память вне кучи: Marshal.AllocHGlobal

- Хэндлы Windows: CreateFile, CreateMutex

- COM объекты

- Выглядят как: IntPtr, void*, HANDLE

23
New cards

HttpClient, FileStream, SqlConnection — управляемые или неуправляемые?

Это управляемые объекты, но они оборачивают неуправляемые ресурсы

FileStream fileStream = new FileStream(...);

// fileStream — управляемый объект в куче

// Внутри хранит IntPtr hFile — неуправляемый хэндл файла

24
New cards

Что такое using и во что разворачивается?

using — синтаксический сахар для автоматического вызова Dispose()

using (var file = new FileStream("test.txt", FileMode.Open))

{

// работа с файлом

}

Разворачивается в:

FileStream file = null;

try

{

file = new FileStream("test.txt", FileMode.Open);

// работа с файлом

}

finally

{

if (file != null)

file.Dispose();

}

Важно: using корректно обработает null!

using (IDisposable resource = null) // не вызовет исключение

{

// код выполнится нормально

}

C# 8.0 — using declaration:

using var file = new FileStream("test.txt", FileMode.Open);

// Dispose вызовется в конце области видимости

25
New cards

Зачем нужен IDisposable, если есть финализатор?

IDisposable — это оптимизация для своевременного освобождения ресурсов

IDisposable (детерминированное освобождение):

- Освобождение ресурсов прямо сейчас

- Контролируемое время и место вызова

- Выполняется на текущем потоке

- Это ОПТИМИЗАЦИЯ — мы не ждем неопределенное время

Финализатор (недетерминированное):

- Неизвестно когда выполнится

- Отдельный поток

- Может вообще не выполниться

Пример важности оптимизации: если только 10 подключений к БД доступно одновременно, без IDisposable они будут заняты до срабатывания финализатора (может быть минуты или часы). С IDisposable — освобождаем сразу после использования

IDisposable позволяет нам явно сказать "я закончил работу с ресурсом СЕЙЧАС", а не полагаться на GC

26
New cards

Если не вызвать Dispose вручную, он вызовется автоматически?

Нет! Dispose НИКОГДА не вызывается автоматически (кроме using)

Частая ошибка:

SqlConnection conn = new SqlConnection(connString);

// ... используем

// Забыли Dispose() — соединение висит до финализатора!

27
New cards

Какие гарантии вызова у Dispose() по сравнению с финализатором?

Dispose:

- Вызывается когда МЫ решили

- Гарантированно выполнится (если мы вызвали)

- Можем обработать исключения

Финализатор:

- Вызывается когда GC решит

- Может не выполниться при завершении процесса

- Исключения игнорируются

28
New cards

Когда использовать Dispose, а когда финализатор?

Dispose: ВСЕГДА для любых ресурсов требующих освобождения

Финализатор: ТОЛЬКО когда напрямую работаем с неуправляемыми ресурсами (практически никогда)

Правило: если используешь чужой класс с Dispose() — вызывай Dispose(). Если пишешь свой класс — финализатор нужен только для прямой работы с IntPtr/хэндлами

29
New cards

Что такое утечки памяти? Откуда они берутся, если есть GC? Как их обнаружить?

Ситуация, когда объекты больше не нужны приложению, но остаются достижимыми от GC Roots

Частые причины:

- Забытая подписка на события

- Статические коллекции без очистки

- Замыкания, захватывающие большие объекты

- Кэши без ограничения размера

Пример утечки через события:

public class Publisher

{

public event Action LongLivedEvent;

}

publisher.LongLivedEvent += myObject.HandleEvent;

// myObject не будет собран, пока жив publisher!

Обнаружение:

- Visual Studio Diagnostic Tools

- JetBrains dotMemory

- PerfView (бесплатный от Microsoft)

30
New cards

Что такое card table при сборке мусора?

Card table — оптимизация для отслеживания ссылок из старших поколений в младшие

Проблема: при сборке Gen 0 нужно найти все ссылки из Gen 1/2 на объекты Gen 0, но сканировать все старшие поколения дорого

Решение:

- Память делится на "карты" по 128 байт

- При записи ссылки JIT помечает карту "грязной"

- При сборке проверяются только грязные карты

Как работает:

class Parent // в Gen 2

{

public Child child;

}

parent.child = new Child(); // Gen 0 объект

// JIT автоматически вставляет код пометки карты

При сборке Gen 0 GC смотрит только помеченные участки Gen 2

31
New cards

Что такое Frozen Object Heap (FOH)?

Специальная куча для неизменяемых объектов, живущих весь срок работы приложения (.NET 8+)

Объекты в FOH:

- Никогда не собираются GC

- Не сканируются при сборке мусора

- Оптимизированы для чтения

- Занимают меньше памяти (нет заголовков для GC)

Что попадает автоматически:

- Строковые литералы

- Type объекты

- Assembly metadata

- Некоторые статические readonly данные

Пример:

// Эта строка в FOH

const string Literal = "Hello";

// Эта строка в обычной куче

string dynamic = new string('H', 5);

32
New cards

Как правильно реализовать паттерн Dispose с финализатором?

Паттерн нужен ТОЛЬКО при прямой работе с неуправляемыми ресурсами (IntPtr, хэндлы). В 99.9% случаев НЕ нужен!

public class UnmanagedWrapper : IDisposable

{

private IntPtr unmanagedHandle;

private bool disposed = false;

public UnmanagedWrapper()

{

unmanagedHandle = NativeMethods.CreateHandle();

}

public void Dispose()

{

Dispose(true);

GC.SuppressFinalize(this);

}

protected virtual void Dispose(bool disposing)

{

if (!disposed)

{

if (disposing)

{

// Освобождаем управляемые ресурсы

// Например, другие IDisposable поля

}

// Освобождаем НЕуправляемые ресурсы

if (unmanagedHandle != IntPtr.Zero)

{

NativeMethods.ReleaseHandle(unmanagedHandle);

unmanagedHandle = IntPtr.Zero;

}

disposed = true;

}

}

// Финализатор ТОЛЬКО если есть неуправляемые ресурсы!

~UnmanagedWrapper()

{

Dispose(false);

}

}

Частая ошибка: добавлять финализатор "на всякий случай" для обычных управляемых ресурсов

+++++