Прокачайся в код-ревью: для первых 50 участников — курс бесплатный

время чтения: 4 мин

Сборка мусора в .NET Framework: как оптимизировать память в приложениях

В статье рассматривается сборка мусора в .NET Framework и объясняется, как происходит процесс, что такое корни приложения, и в чем разница между Finalization queue и F-reachable queue.

Автор статьи — Lead Software Engineer EPAM Продипто Банерджи.

Что такое сборка мусора в .NET Framework

Сборка мусора относится к автоматическому процессу управления памятью, который связан с выделением и освобождением памяти для объектов, созданных приложениями. Когда приложениям требуется память, сборщик мусора (англ. garbage collector, GC) выделяет ее для объекта из управляемой кучи.

В .NET Framework есть среда выполнения — общеязыковая исполняющая среда (англ. common language runtime, CLR). Она выполняет код и предоставляет сервисы для облегчения разработки. В среде CLR сборщик мусора отвечает за процесс автоматического управления памятью.

Пример 1

Это изображение наглядно демонстрирует, что в управляемой куче в настоящее время хранятся 2 объекта (объект A и объект B), и NextObjPtr указывает на следующее доступное место для хранения объектов в куче.

Что такое сборка мусора в Java и как она работает?

Корни приложения

В каждом приложении .NET есть набор корней. Эти корни служат ссылками на места хранения, указывая на объекты в управляемой куче или на объекты, которые в настоящее время имеют значение null. В основном корни приложения выполняют функции индексов, специфичных для данного приложения, и обеспечивают доступ к управляемой куче.

Корни приложения включают в себя все глобальные и статические указатели объектов в приложении. Кроме того, частью корней приложения также считаются любые локальные переменные или указатели объектов параметров, находящиеся в стеке потока. Даже регистры процессора, содержащие указатели на объекты в куче, входят в корни приложения. Среда CLR хранит и контролирует список активных корней, доступных для алгоритма сборщика мусора.

Пример 2

На изображении показано, как объекты хранятся в управляемой куче, в то время как корни приложения служат ссылками, которые указывают на места хранения этих объектов, что необходимо для функционирования приложения.

Как только управляемая куча заполняется, запускается сборщик мусора для освобождения памяти. В процессе выполнения он сначала рассматривает все объекты в куче как мусор, а затем проходит по корням и строит граф, который включает все объекты, доступные из этих корней. Этот процесс повторяется для всех последующих корней, пока граф не будет сформирован полностью.

Примечательно, что сборщик мусора гарантирует, что он не дублирует объекты, уже присутствующие в графе. Такой подход повышает производительность и предотвращает бесконечные циклы, например, в кольцевых связных списках.

Пример 3

Задача сборщика — отделить объекты, являющиеся мусором, от объектов, которые к мусору не относятся. Сборщик перемещает объекты, которые не являются мусором, в новое место, сжимая используемое пространство и повышая эффективность его использования. Соответственно, сборщик также обновляет корни приложения, гарантируя, что указатели теперь ссылаются на новые местоположения объектов.

Очередь финализации (Finalization queue) и очередь завершения (F-reachable queue)

Когда приложение создает новый объект, сборщик мусора выделяет память из кучи. Если тип объекта включает finalize метод, то ссылка на объект помещается в Finalization queue — очередь финализации. Эта очередь представляет собой внутреннюю структуру данных, контролируемую сборщиком мусора. Каждая запись в очереди указывает на объект, для которого необходимо вызвать его finalize метод, прежде чем память, выделенная для этого объекта, может быть освобождена.

Пример 4

Предположим, сборщик мусора определил объекты G, D и C как мусор. В процессе сборки он сканирует очередь финализации для поиска указателей на эти объекты. При обнаружении указателя он удаляет объект из очереди финализации и перемещает его в F-reachable queue — очередь завершения.

Очередь завершения выполняет функции еще одной внутренней структуры данных, контролируемой сборщиком мусора.

Пример 5

Каждая запись в очереди завершения соответствует объекту, finalize метод которого может быть вызван. Таким образом, память объектов B и H, у которых нет finalize метода, будет освобождена. Однако память объекта G не будет освобождена, так как его finalize метод еще не вызван.

Выполнение finalize методов происходит в отдельном потоке. Когда очередь завершения пуста, этот поток переходит в режим ожидания. Однако когда в очереди завершения появляется запись, поток приступает к выполнению finalize метода объекта. После этого объект удаляется из очереди завершения. Из-за такого поведения рекомендуется не выполнять никакой код внутри finalize метода, чтобы избежать потенциальных проблем.

Пример 6

Вывод

Общеязыковая исполняющая среда инициирует сборку мусора — автоматический процесс, который использует встроенные очереди для выполнения finalize метода, связанного с объектами. Для разработчиков важно исключить выполнение кода в finalize методе, чтобы избежать возможных проблем.

Помимо этого, рекомендуется удалять экземпляр объекта сразу после того, как он выполнил поставленную задачу или завершил работу. Следуя такому подходу, управляемая куча не будет заполнена «забытыми» объектами.