Smalltalk по-русски
вторник, Апрель 26, 2005
[VW] Знаете ли Вы, что такое Ephemeron?
Проблемы "простого" сборщика мусора

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

Самый распространённый пример такой задачи - хранение подписчиков на события объектов. Первоначально в ST использовалась очень простая схема, когда для добавления подписчиков используется сообщение #addDependent: с аргументом - подписчиком на события. Сейчас схема усложнилась - появилась возможность подписываться не на все события подряд, а только на те, которые интересуют, но принципиально ничего не поменялось - есть объект публикующий события и объекты-подписчики обрабатывающие события. Первое, что приходит в голову - завести переменную для хранения коллекции объектов-подписчиков. С этой схемой особых проблем нет. Коллекция объектов умрёт вместе с объектом публикующим события, а значит объекты-подписчики не будут удерживаться дольше чем нужно. Именно так и устроен класс Model. Однако, в ST подписаться на некие события можно не только у модели, а у любого объекта (простейший пример - WeakArray уведомляет своих подписчиков о смерти хранимых в нём объектов). Тут можно либо попробовать заводить переменную объекта для подписчиков в каждом классе, который может публиковать события, либо разрешить проблему сразу для всей системы задействовав класс Object. Однако, заводить переменную объекта для хранения подписчиков в классе Object не только нецелесообразно, но и невозможно в текущих реализациях виртуальных машин (ВМ).

Раз нельзя завести переменную, то прийдётся идти другим путём. А именно, заведём глобальный словарь где ключом будет объект публикующий события, а связанным с ключом значением будет коллекция подписчиков. Такие словари, где ключ это некий объект, а значение это присоединённое к объекту свойство, зачастую называют реестрами. Решение это вроде бы универсальное, но реестр то глобальный.

Иллюстрация словаря на сильных ссылках
А значит живёт он вечно, и, вместе с ним будут удерживаться и все объекты, и публикующие события и подписавшиеся на них. И если от событий не отписываться, то возникнет утечка памяти. О том, к каким проблемам приводила подобная схема говорит тот факт, что в VW для инспектирования подобного словаря существует отдельный пункт меню "Browse -> Inspect -> DependentsFields" и все рекомендации по борьбе с утечками советуют в первую очередь проверить именно этот глобальный словарь.

Для борьбы с такими ситуациями и придумали слабые ссылки (weak references). Попробуем задействовать слабые ссылки для реализации нашего реестра. Что же получится, если для хранения ключей словаря использовать слабые ссылки?

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

Попытка использовать для слабые ссылки для хранения значений еще более нелепа.

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

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

Иллюстрация словаря на эфемеронах
А именно, ссылка на ключ слабая. Ссылка же на значение должна быть сильной, но только до тех пор, пока есть строгие ссылки на ключ "со стороны", то есть, не идущие от самого словаря. После того, как ключ может стать "мусором" должно быть освобождено и значение. Именно такое поведение обеспечивают обычные переменные объекта - значение переменной освобождается по уничтожении объекта в котором это значение хранится. Подобное поведение и обеспечивают эфемероны (ephemerons).

Суть эфемеронов

Эфемероны это разновидность ассоциации, то есть пары ключ-значение, и слабой ссылки. Поведение эфемерона такое: если есть сильные ссылки и на сам эфемерон и есть ссылки на ключ, ведущие не из самого эфемерона, то значение эфемерона удерживается. Если есть сильные ссылки на эфемерон, но нет сильных ссылок на ключ, исключая ссылки, ведущие из самого эфемерона (например от "значения" на "ключ"), то эфемерон уведомляется об этом и может обработать эту ситуацию, например, просто освободить значение или выполнить его финализацию. Обратите внимание, что эфемерон уведомляется только в том случае, если на него самого есть сильные ссылки.

Эфемероны были придуманы Георгом Босвортом (George Bosworth) из Digitalk для Visual Smalltalk Enterpraise и описаны Барри Хэесом (см. "Ephemerons: a new finalization mechanism; Barry Hayes; Proceedings of the 1997 ACM SIGPLAN conference on Object-oriented programming systems, languages and applications, 1997, Pages 176 - 183"). В VW эфемероны реализованы Барри Хэесом и Элиотом Мирэндой.

Помимо VW существует реализация эфемеронов для Squeak (упрощенный вариант, когда все ссылки просто об-nil-яются при исчезновении ссылок на ключ); эфемероны есть в GNU Smalltalk начиная с версии 2; эфемероны используются в XEmacs; очень похожий механизм, называемый "key/value weak pointers", существует в Haskell.

Эфемероны в VW

Для начала напомню, что в VW могут существовать классы, имеющие различную природу. Тип класса задаётся при его создании параметром indexedType:. Допустимые значения параметра: #none, #objects, #bytes, #immediate, #weak и #ephemeron. По-умолчанию тип проставляется в #none, и, поскольку прикладным программистам нет нужды создавать классы прочих типов, то описание значения этих типов будет дано другим разом. Сейчас же остановимся на типе #ephemeron.

Класс, создаваемый с indexedType: выставленным в #ephemeron, должен содержать минимум одну переменную экземпляра. Допустимо, что это переменная унаследованная от какого либо родительского класса. Такой класс должен быть унаследован от класса с типом #none и подклассы должны быть только типа #ephemeron.

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

Эфемероны и финализация в VW

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

Эфемероны в VW поддерживают пообъектную финализацию. После того, как сборщик мусора находит живые эфемероны у которых на ключ (первую по порядку переменную) нет больше ссылок, этим эфемеронам посылается сообщение #mourn. Если на эфемерон нет ссылок, то сообщение #mourn ему не посылается. В VW уже существуют стандартные реализации классов с "indexedType: #ephemeron", которые содержат ряд методов управляющих финализацией. Это WeakKeyAssociation и его потомок класс Ephemeron.

Объекты класса WeakKeyAssociation после выполнения метода #mourn начинают возвращать false на сообщение #isActiveEphemeron. Если такому объекту послать сообщение #isActiveEphemeron: true, то процедура финализации может быть запущена повторно. Метод #mourn в классе WeakKeyAssociation просто проставляет nil в полях "ключ" и "значение".

Поведение потомка WeakKeyAssociation, класса Ephemeron, более сложное. Оно позволяет, кроме выполнения действий по очистке полей эфемерона, освободить так же и сам эфемерон. Одна из переменных экземпляра в классе Ephemeron - manager. По умолчанию, метод #mourn просто посылает менеджеру сообщение #mournKeyOf: self. Класс EphemeronDictionary это словарь, в котором в качестве ассоциаций используются эфемероны. При создании ассоциации в EphemeronDictionary, словарь создаёт эфемерон в котором менеджер это сам словарь. Таким образом, словарь может получить уведомление о том, что его эфемерон готов "умереть" через сообщение #mournKeyOf:. Сам словарь так же позволяет изменить менеджер у всех своих эфемеронов через сообщение #manager:. По-умолчанию, словарь просто удаляет ссылку на "умерший" эфемерон, позволяя сборщику мусора уничтожить граф объектов с эфемероном во главе. И так, чтобы обработать уведомление о том, что на ключ эфемерона больше нет ссылок есть несколько вариантов:

  • создать свой класс c "indexedType: #ephemeron" с процедурой #mourn;
  • создать подкласс класса Ephemeron и переопределить метод #mourn;
  • создать подкласс класса EphemeronDictionary и переопределить метод #mournKeyOf:;
  • создать свой класс с методом #mournKeyOf: и задать его в качестве менеджера в сам эфемерон или в словарь эфемеронов.
Обратите особое внимание, что сообщение #mourn будет послано только тому эфемерону на который есть сильные ссылки. Это сделано для того, чтобы избежать необходимости "оживлять" эфемероны. Именно по-этому, в VW эфемероны используются только опосредованно, через класс EphemeronDictionary.

Ярлыки:

Comments: Отправить комментарий

<< Home

Популярные статьи
:: Smalltalk?!
:: Почему Smalltalk?
:: Great Leap Forward from Java to Smalltalk

Последние сообщения
:: [VW] Полезняшки: StorePlugins
:: Dolphin 6 - IdeaSpace
:: Находим публичный интерфейс класса во время выполн...
:: 1-е ежегодное соревнование программистов на Smallt...
:: Ruby/Python on Smalltalk VM
:: [Job] Работа в Transas (Санкт-Петербург)
:: [VW] Полезняшки: Teachable
:: Smalltalk/JVM - open source?
:: Ambrai Smalltalk 1.0.6 Beta
:: [VW] Знаете ли Вы, что такое pragma?

Архив
Предыдущие новости / Декабрь 2004 / Январь 2005 / Февраль 2005 / Март 2005 / Апрель 2005 / Май 2005 / Июнь 2005 / Июль 2005 / Август 2005 / Сентябрь 2005 / Октябрь 2005 / Ноябрь 2005 / Декабрь 2005 / Январь 2006 / Февраль 2006 / Март 2006 / Апрель 2006 / Май 2006 / Июнь 2006 / Июль 2006 / Сентябрь 2006 / Октябрь 2006 / Ноябрь 2006 / Декабрь 2006 / Январь 2007 / Февраль 2007 / Март 2007 / Апрель 2007 / Май 2007 / Июнь 2007 / Август 2007 / Сентябрь 2007 / Ноябрь 2007 / Январь 2008 / Март 2008 / Май 2008 / Июнь 2008 / Июль 2008 / Август 2008 / Сентябрь 2008

Atom Feed
Smalltalk по-русски


Powered by Blogger