То что спецификацию (как минимум первые версии) для языка JavaScript писали не очень умные люди с лихвой компенсируется мозгами тех кто пишет V8. Чем больше разбираюсь в проекте - тем больше восхищаюсь этими людьми. И так - как же GC V8 узнает о ссылках на объекты в куче из C++ кода ?
Вся работа с объектами размещенными в куче построена на так называемых хендлерах. Так как GC может перемещать объекты размещенные в куче во время сборки мусора прямая работа с этими объектами (по указателю) запрещена. Все обращения только через хендлеры. Этих хендлеров есть несколько типов, все они объявлены в v8.h
- Local - это хендлер время жизни которого ограничено временем жизни области видимости в которой он объявлен. Как только мы выходим из области видимости - стэк отматывается и хендлер становится недействительным.
- Persistent - это хендлер время жизни которого не контролируется областью видимости. Живет до тех пока явно не вызовешь Reset()
- Global - тоже самое что и Persistent но c поддержкой move семантики
- Eternal - это хендлер используемый для "вечных" ссылок. То есть ссылок живущих до тех пор пока жив Isolate. Они немного более удобны с точки зрения GC чем Persistent
- TracedGlobal - тоже самое что и Global но с подержкой трассировки GC. Для чего он может использоваться честно до конца не разобрался
Вся магия по управлению Local хэндлерами через область видимости реализуюется в классе HandleScope и Isolate. Инстанс HandleScope обычно создается до создания первого хендлера. Все созданные после этого Local хэндлеры создаются в этой области видимости. HandleScope могут быть вложенными. При выходе за границы области видимости вызывается деструктор HandleScope в котором происходит вызов HandleScope::CloseScope() который подчищает Local хэндлеры созданные в этой области видимости.
Тут собственно и начинается самое интересное. Выясняется что внутри Isolate (экземпляр виртуальной машины V8 со своей кучей и всем остальным) есть список буферов в которых собственно размещаются указатели на объекты размещенные в куче. val_ который хранится в Local хендлере это указатель на этот адрес, размещенный в буфере Isolate. Каждый раз когда мы создаем новый хендлер HandleScope::CreateHandle() выделает в буфере класса Isolate новый указатель который указывает на кучу. А возвращает указатель на этот указатель. Собственно когда нужно собрать мусор, GC пробегает по этому списку буферов заполнгенному указателями на кучу и помечает объекты как используемые. При перемещении объектов GC также меняет указатели если кто-то ссылается на этот объект. В общем получается весьма эффективная система.
Комментариев нет:
Отправить комментарий