ECMAScript® 2024 Language Specification

Draft ECMA-262 / February 15, 2024

9.10 Processing Model of WeakRef and FinalizationRegistry Targets

9.10.1 Objectives

This specification does not make any guarantees that any object or symbol will be garbage collected. Objects or symbols which are not live may be released after long periods of time, or never at all. For this reason, this specification uses the term "may" when describing behaviour triggered by garbage collection.

The semantics of WeakRefs and FinalizationRegistrys is based on two operations which happen at particular points in time:

  • When WeakRef.prototype.deref is called, the referent (if undefined is not returned) is kept alive so that subsequent, synchronous accesses also return the same value. This list is reset when synchronous work is done using the ClearKeptObjects abstract operation.
  • When an object or symbol which is registered with a FinalizationRegistry becomes unreachable, a call of the FinalizationRegistry's cleanup callback may eventually be made, after synchronous ECMAScript execution completes. The FinalizationRegistry cleanup is performed with the CleanupFinalizationRegistry abstract operation.

Neither of these actions (ClearKeptObjects or CleanupFinalizationRegistry) may interrupt synchronous ECMAScript execution. Because hosts may assemble longer, synchronous ECMAScript execution runs, this specification defers the scheduling of ClearKeptObjects and CleanupFinalizationRegistry to the host environment.

Some ECMAScript implementations include garbage collector implementations which run in the background, including when ECMAScript is idle. Letting the host environment schedule CleanupFinalizationRegistry allows it to resume ECMAScript execution in order to run finalizer work, which may free up held values, reducing overall memory usage.

9.10.2 Liveness

For some set of objects and/or symbols S a hypothetical WeakRef-oblivious execution with respect to S is an execution whereby the abstract operation WeakRefDeref of a WeakRef whose referent is an element of S always returns undefined.

Note 1
WeakRef-obliviousness, together with liveness, capture two notions. One, that a WeakRef itself does not keep its referent alive. Two, that cycles in liveness does not imply that a value is live. To be concrete, if determining v's liveness depends on determining the liveness of a WeakRef referent, r, r's liveness cannot assume v's liveness, which would be circular reasoning.
Note 2
WeakRef-obliviousness is defined on sets of objects or symbols instead of individual values to account for cycles. If it were defined on individual values, then a WeakRef referent in a cycle will be considered live even though its identity is only observed via other WeakRef referents in the cycle.
Note 3
Colloquially, we say that an individual object or symbol is live if every set containing it is live.

At any point during evaluation, a set of objects and/or symbols S is considered live if either of the following conditions is met:

  • Any element in S is included in any agent's [[KeptAlive]] List.
  • There exists a valid future hypothetical WeakRef-oblivious execution with respect to S that observes the identity of any value in S.
Note 4
The second condition above intends to capture the intuition that a value is live if its identity is observable via non-WeakRef means. A value's identity may be observed by observing a strict equality comparison or observing the value being used as key in a Map.
Note 5

Presence of an object or a symbol in a field, an internal slot, or a property does not imply that the value is live. For example if the value in question is never passed back to the program, then it cannot be observed.

This is the case for keys in a WeakMap, members of a WeakSet, as well as the [[WeakRefTarget]] and [[UnregisterToken]] fields of a FinalizationRegistry Cell record.

The above definition implies that, if a key in a WeakMap is not live, then its corresponding value is not necessarily live either.

Note 6
Liveness is the lower bound for guaranteeing which WeakRefs engines must not empty. Liveness as defined here is undecidable. In practice, engines use conservative approximations such as reachability. There is expected to be significant implementation leeway.

9.10.3 Execution

At any time, if a set of objects and/or symbols S is not live, an ECMAScript implementation may perform the following steps atomically:

  1. For each element value of S, do
    1. For each WeakRef ref such that ref.[[WeakRefTarget]] is value, do
      1. Set ref.[[WeakRefTarget]] to empty.
    2. For each FinalizationRegistry fg such that fg.[[Cells]] contains a Record cell such that cell.[[WeakRefTarget]] is value, do
      1. Set cell.[[WeakRefTarget]] to empty.
      2. Optionally, perform HostEnqueueFinalizationRegistryCleanupJob(fg).
    3. For each WeakMap map such that map.[[WeakMapData]] contains a Record r such that r.[[Key]] is value, do
      1. Set r.[[Key]] to empty.
      2. Set r.[[Value]] to empty.
    4. For each WeakSet set such that set.[[WeakSetData]] contains value, do
      1. Replace the element of set.[[WeakSetData]] whose value is value with an element whose value is empty.
Note 1

Together with the definition of liveness, this clause prescribes optimizations that an implementation may apply regarding WeakRefs.

It is possible to access an object without observing its identity. Optimizations such as dead variable elimination and scalar replacement on properties of non-escaping objects whose identity is not observed are allowed. These optimizations are thus allowed to observably empty WeakRefs that point to such objects.

On the other hand, if an object's identity is observable, and that object is in the [[WeakRefTarget]] internal slot of a WeakRef, optimizations such as rematerialization that observably empty the WeakRef are prohibited.

Because calling HostEnqueueFinalizationRegistryCleanupJob is optional, registered objects in a FinalizationRegistry do not necessarily hold that FinalizationRegistry live. Implementations may omit FinalizationRegistry callbacks for any reason, e.g., if the FinalizationRegistry itself becomes dead, or if the application is shutting down.

Note 2

Implementations are not obligated to empty WeakRefs for maximal sets of non-live objects or symbols.

If an implementation chooses a non-live set S in which to empty WeakRefs, this definition requires that it empties WeakRefs for all values in S simultaneously. In other words, it is not conformant for an implementation to empty a WeakRef pointing to a value v without emptying out other WeakRefs that, if not emptied, could result in an execution that observes the value of v.

9.10.4 Host Hooks

9.10.4.1 HostEnqueueFinalizationRegistryCleanupJob ( finalizationRegistry )

The host-defined abstract operation HostEnqueueFinalizationRegistryCleanupJob takes argument finalizationRegistry (a FinalizationRegistry) and returns unused.

Let cleanupJob be a new Job Abstract Closure with no parameters that captures finalizationRegistry and performs the following steps when called:

  1. Let cleanupResult be Completion(CleanupFinalizationRegistry(finalizationRegistry)).
  2. If cleanupResult is an abrupt completion, perform any host-defined steps for reporting the error.
  3. Return unused.

An implementation of HostEnqueueFinalizationRegistryCleanupJob schedules cleanupJob to be performed at some future time, if possible. It must also conform to the requirements in 9.5.