ECMAScript | Модель обработки объектов WeakRef и FinalizationRegistry

ECMAScript | Модель обработки объектов WeakRef и FinalizationRegistry

Цели

Эта спецификация не дает никаких гарантий, что какой-либо объект будет удален сборщиком мусора. Нерабочие объекты могут быть освобождены по прошествии длительного времени или вообще никогда. По этой причине в данной спецификации используется термин «может» (may) при описании поведения, вызванного сборкой мусора.

Семантика объектов WeakRef и FinalizationRegistry основана на двух операциях, которые происходят в определенные моменты времени:

  • Когда вызывается WeakRef.prototype.deref, референт (если undefined не возвращается) остается активным, так что последующие синхронные обращения также возвращают объект. Этот список сбрасывается, когда синхронная работа выполняется с использованием абстрактной операции ClearKeptObjects.
  • Когда объект, зарегистрированный в FinalizationRegistry, становится недоступным, в конечном итоге может быть выполнен вызов обратного вызова очистки FinalizationRegistry после завершения синхронного выполнения ECMAScript. Очистка FinalizationRegistry выполняется с помощью абстрактной операции CleanupFinalizationRegistry.

Ни одно из этих действий (ClearKeptObjects или CleanupFinalizationRegistry) не может прервать синхронное выполнение ECMAScript. Поскольку хосты могут собирать более длинные синхронные запуски ECMAScript, эта спецификация откладывает планирование ClearKeptObjects и CleanupFinalizationRegistry на среду хоста.

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

 

Живучесть

Для некоторого набора объектов S гипотетическое выполнение без учета WeakRef (hypothetical WeakRef-oblivious) относительно S — это выполнение, при котором абстрактная операция WeakRefDeref объекта WeakRef, референт которого является элементом S, всегда возвращает значение undefined.

Примечание 1

WeakRef-забвение вместе с живостью охватывают два понятия. Во-первых, WeakRef сам по себе не поддерживает жизнь объекта. Во-вторых, циклы жизненности не означают, что объект жив. Чтобы быть конкретным, если определение жизнеспособности obj зависит от определения жизнеспособности другого референта WeakRef, obj2, жизнеспособность obj2 не может предполагать жизнеспособность obj, что было бы круговым рассуждением.

Примечание 2

WeakRef-забвение определяется для наборов объектов вместо отдельных объектов для учета циклов. Если он был определен для отдельных объектов, то объект в цикле будет считаться активным, даже если его значение Object наблюдается только через WeakRefs других объектов в цикле.

Примечание 3

В просторечии мы говорим, что отдельный объект является живым, если каждый набор объектов, содержащий его, является живым.

В любой момент во время оценки набор объектов S считается живым (live), если выполняется одно из следующих условий:

  • Любой элемент в S включается в Список любого Агента [[KeptAlive]].
  • Существует допустимое будущее гипотетическое выполнение с игнорированием WeakRef по отношению к S, которое наблюдает за значением Object любого объекта в S.
Примечание 4

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

Примечание 5

Наличие объекта в поле, внутреннем слоте или свойстве не означает, что объект активен. Например, если объект, о котором идет речь, никогда не передается обратно в программу, его нельзя наблюдать.

Это касается ключей в WeakMap, членов WeakSet, а также полей [[WeakRefTarget]] и [[UnregisterToken]] записи FinalizationRegistry Cell.

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

Примечание 6

Живучесть — это нижняя граница, гарантирующая, что движки WeakRefs не должны опустошаться. Живучесть, как определено здесь, неразрешима. На практике движки используют консервативные приближения, такие как достижимость. Ожидается значительная свобода действий при реализации.

 

Выполнение

В любое время, если набор объектов S не активен, реализация ECMAScript может выполнять следующие шаги атомарно:

1. Для каждого элемента obj из S выполните
   a. Для каждой ссылки WeakRef ref, такой что ref.[[WeakRefTarget]] является obj, выполните
      i. Установите для ref.[[WeakRefTarget]] пустое значение - empty.
   b. Для каждой FinalizationRegistry fg такой, что fg.[[Cells]] содержит ячейку Записи cell, такую что cell.[[WeakRefTarget]] является obj, выполните
      i. Установите ячейку cell.[[WeakRefTarget]] на пустое значение - empty.
      ii. По желанию выполнить ! HostEnqueueFinalizationRegistryCleanupJob(fg).
   c. Для каждой карты map WeakMap, такой что map.[[WeakMapData]] содержит Запись r такую, что r.[[Key]] является obj, выполните
      i. Установите r.[[Key]] в пустое значение - empty.
      ii. Установите r.[[Value]] в пустое значение - empty.
   d. Для каждого набора set WeakSet, такого что set.[[WeakSetData]] содержит obj, выполните
      i. Замените элемент set.[[WeakSetData]], значение которого равно obj, на элемент, значение которого пусто - empty.
Примечание 1

Вместе с определением живучести этот пункт предписывает юридические оптимизации, которые реализация может применять в отношении WeakRefs.

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

С другой стороны, если идентичность объекта является наблюдаемой, и этот объект находится во внутреннем слоте [[WeakRefTarget]] WeakRef, запрещены такие оптимизации, как рематериализация, при которой WeakRef является видимым пустым.

Поскольку вызов HostEnqueueFinalizationRegistryCleanupJob является необязательным, зарегистрированные объекты в FinalizationRegistry не обязательно содержат этот FinalizationRegistry в живом. Реализации могут опускать обратные вызовы FinalizationRegistry по любой причине, например, если сам FinalizationRegistry становится мертвым или если приложение завершает работу.

Примечание 2

Реализации не обязаны очищать WeakRefs для максимальных наборов не-живых объектов.

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

 

Хост зацепки

HostEnqueueFinalizationRegistryCleanupJob ( finalizationRegistry )

Абстрактная операция HostEnqueueFinalizationRegistryCleanupJob принимает аргумент finalizationRegistry (FinalizationRegistry). HostEnqueueFinalizationRegistryCleanupJob — это абстрактная операция, определяемая реализацией, которая, как ожидается, вызовет CleanupFinalizationRegistry(finalizationRegistry) в какой-то момент в будущем, если это возможно. Ответственность хоста состоит в том, чтобы сделать этот вызов в то время, которое не прерывает синхронное выполнение кода ECMAScript.

 

Информационные ссылки

Предыдущая тема — ECMAScript | Продвижение Вперед

Стандарт ECMAScript — Раздел «9.9 Processing Model of WeakRef and FinalizationRegistry Objects» — https://tc39.es/ecma262/#sec-weakref-processing-model