Сборщик мусора

89

В этой и последующих статьях мы займемся исследованием сборщика мусора в .NET (Garbage Collector, GC), одного из основных механизмов, оказывающих существенное влияние на производительность приложений для .NET. Освобождая разработчиков от хлопот, связанных с освобождением памяти, сборщик мусора вводит другие проблемы, которые необходимо учитывать при разработке программ, производительность которых имеет большое значение.

Назначение сборщика мусора

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

При создании сборщика мусора в .NET преследовались две основные цели:

В существующих языках программирования и фреймворках используются различные стратегии управления памятью. Мы коротко исследуем две из них: управление на основе списка свободных блоков (реализацию которой можно найти в коллекции стандартных инструментов управления памятью языка C) и сборка мусора на основе подсчета ссылок.

Управление свободным списком

Управление на основе списка свободных блоков - это механизм управления распределением памяти в стандартной библиотеке языка C, который также по умолчанию используется функциями управления памятью в C++, такими как new и delete. Это детерминированный диспетчер памяти, при использовании которого вся ответственность за выделение и освобождение памяти ложится на плечи разработчика. Свободные блоки памяти хранятся в виде связанного списка, откуда изымаются блоки памяти при выделении, и куда они возвращаются, при освобождении.

Диспетчер памяти хранит список свободных блоков

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

Механизм управления памятью на основе списка не свободен от тактических и стратегических решений, влияющих на производительность приложения. Ниже перечислены некоторые из них:

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

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

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

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

Сборка мусора на основе подсчета ссылок

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

Каждый объект содержит счетчик ссылок

Одним из примеров управления памятью на основе подсчета ссылок в экосистеме Windows является объектная модель программных компонентов (Component Object Model, COM). Объекты COM снабжаются счетчиками ссылок, определяющими продолжительность их существования. Когда значение счетчика ссылок достигает 0, объект может освободить занимаемую им память. Основное бремя подсчета ссылок ложится на плечи разработчика, в виде явного вызова методов AddRef() и Release(), хотя в большинстве языков имеются обертки, автоматизирующие вызовы этих методов при создании и удалении ссылок.

Ниже перечислены основные проблемы, связанные с управлением памятью на основе подсчета ссылок:

Уничтожение циклических ссылок

В следующей статье мы рассмотрим механизм сборки мусора на основе трассировки, который используется в .NET.

Пройди тесты
Лучший чат для C# программистов