Очевидным недостатком подсчета ссылок являются издержки выполнения как временные, так и по объему памяти. Для всех операций со ссылками реализация языка должна выполнять арифметическую операцию, а в случае отсоединения, - условный оператор. К тому же, к каждому объекту добавляется поле счетчика ссылок.
Но есть более серьезная проблема, делающая подсчет ссылок, к сожалению, мало используемым. ("К сожалению", поскольку эта техника легко реализуема.) Проблема связана с циклическими структурами. Рассмотрим в очередной раз наш основной пример структуры с взаимосвязанными объектами:
Рис. 9.14. Неудаляемая при подсчете ссылок циклическая структура
Объекты О1, О2 и О3 содержат циклические ссылки друг на друга. Допустим, что нет объектов вне структуры кроме О, содержащих ссылки на какой-либо из объектов структуры. Соответствующий счетчик ссылок показан под каждым объектом.
Теперь допустим, что ссылка от О к О1 отрезана, например потому что подпрограмма вызываемая с целью О выполняет инструкцию:
а:=void
Тогда объекты О1, О2, О3 станут недостижимыми, но механизм подсчета ссылок не определит эту ситуацию: вышеуказанная инструкция уменьшит счетчик ссылок О1 до трех и только. Счетчики всех трех объектов останутся положительными, что не позволит определить необходимость их утилизации.
Из-за этой проблемы, подсчет ссылок применим только к структурам, гарантированно не использующим циклы. Это делает его неподходящим в качестве универсального механизма на уровне реализации языка. Невозможно гарантировать, что произвольная система не создает циклических структур. Поэтому метод может быть применен только при создании библиотек компонентов. К сожалению, если методы уровня компонентов, рассмотренные в предыдущем разделе, не применимы, то это происходит потому, что используемые структуры слишком сложны и, чаще всего, по причине наличия циклов.
Сборка мусора
Наиболее общей и полностью удовлетворительной техникой является лишь автоматическая сборка мусора или просто сборка мусора.
Механизм сборки мусора
Сборщик мусора (garbage collector) - это функция исполнительной системы (runtime system) языка программирования. Сборщик мусора выполняет обнаружение и утилизацию недостижимых объектов, не нуждаясь в управлении приложением, хотя приложение может иметь в своем распоряжении различные средства контроля работы сборщика.
Детальное рассмотрение всех проблем сборки мусора требует отдельной книги. (В конце лекции приведена библиография по этой проблеме.) Рассмотрим общие принципы и возникающие проблемы, концентрируя внимание на свойствах, важных для разработчиков программ.
Требования к сборщику мусора
Сборщик мусора, несомненно, должен быть корректным, удовлетворяя двум требованиям:
Свойства сборщика мусора
Качественность: каждый собираемый объект должен быть недостижимым.
Полнота: каждый недостижимый объект должен быть собран.
Качественность - абсолютное требование: лучше не собирать мусор, чем выбрасывать нужный объект. Нужна полная уверенность в том, что управлению памятью можно слепо доверять. Фактически надо забыть о нем почти навсегда, будучи уверенным, что кто-то как-то убирает беспорядок в вашей программе, также как кто-то как-то убирает мусор в вашем офисе, когда вас нет, но не убирает при этом ваши книги, компьютер и семейные фотографии со стола.
Полнота желательна - без нее все равно можно столкнуться с проблемой, которую сборщик мусора должен решить: память тратится на бесполезные объекты. Но здесь можно не требовать безупречности: сборщик может быть полезным, если он собирает основную часть мусора, иногда пропуская один или два объекта.
Это замечание требует детализации. На практике любой сборщик промышленного масштаба должен обладать полнотой. Полнота на практике также необходима, как качественность, но менее жестка, если перефразировать ее определение: "каждый недостижимый объект должен быть, в конце концов, собран". Предположим, что мы можем сделать процесс сборки более эффективным, благодаря алгоритму, который собирает каждый недостижимый объект, но может запоздать с обращением к некоторым из них: такая схема будет приемлемой для большинства приложений. В этом идея обсуждаемого далее алгоритма "сборки мусора поколений", который в целях эффективности чаще сканирует области памяти, содержащие с большей вероятностью недостижимые объекты, и реже обращает внимание на другие участки памяти.