I just had a discussion with maintainers of WASM targeting PLs around the fact the unpredictability of the FinalizationRegistry is a concrete issue for them.
- the WASM engine has a limited (and known) amount of buffer to deal with
- the V8 (or others) engine has an "infinite" (hardware depending) amount of RAM to deal with
- the V8 GC triggers potentially too late to help proxied referenced to be cleared in the WASM engine
- there is no way to hint the FinalizationRegistry that some reference should be freed more periodically
The only similar discussion I've previously found is Exposing existing GC functionality with GC.criticalHint and GC.collectHint: pause hints for realtime JavaScript - #18 by claudiameadows but it's from 2021 and times the FinalizationRegistry API was (IIRC) non-existent.
Prior Art
As absurd or unrelated as it sounds, the AbortController
mechanism on the Web, hence signals on a fetch operation, has already landed as specification.
That API grants the following:
- only the ArbortController owner can dictate the faith of a
fetch
operation and drop it - the
signal
primitive to do so works privately behind the scene and it's a very well known reference that, once it's.abort()
is called, breaks the current fetch operation
Because I understand that observability is an issue for GC but also because I believe that's actually desired in more convoluted cross worker, cross realm, or cross Programming Language and interpreters use cases, I find the AbortController related mechanism a great fit for the idea I am going to propose.
Hinted GC via FinalizationRegistry
The idea is that the current constructor accepts a callback as unique field in its constructor and it throws when such parameter/field is not a callback ... there's room to play here without breaking the world as that's usually how new proposal needs to land ... so that even if I think new FinalizationRegistry(callback, gcHintSignal)
would be ideal, or even new FinalizationRegistry(callback, {signal})
to mimic an already understood API out there, we can have without issues a new FinalizationRegistry({ signal, invoke })
(invoke or callback or handleEvent or whatever) that would never cause unexpected issues.
The new signature would look like this:
const signal = new GCHint(); // or GCController
const fr = new FinalizationRegistry({
callback() { console.log('triggered'); },
signal // or hint
});
After that, whenever is needed, desired or whatnot:
signal.hint()
The result of that operation is that any reference registered via that fr
registry would have an easier life to be freed from the queue of stuff that might eventually be freed in the future and, if that reference is gone, the registry can invoke the related callback.
As Summary
- this idea won't expose to anyone easy observability as nobody can know if a
signal
has ever be passed to a new FinalizationRegistry (as well as not necessarily everyone has a held reference to observe) - the moment any reference passes through more than one FinalizationRegistry the first signal that invokes
.hint()
simply triggers other callbacks too around that object ... after all, anything devs need to know is that such reference is gone for good - every
hint()
call is meant to be greedy and blocking but that's what GC does anyway when it collects and kicks callbacks - WASM and other use cases heavily GC related can at least
hint()
periodically while running, hoping to have more memory freed sooner than later - nobody else that used FinalizationRegistry to date would care or would be affected by this new possibility, actually they might welcome more incremental releases (as side-effect of this proposal)
- last but not least, engines are free to ignore the
signal.hint()
call without affecting those engines that actually would instead care about it ... so it still can be written thathint()
doesn't guarantee anything at all, it's just, literally, a hint for the GC - the hint() in the GC would result in it running sooner than later, no major architectural changes needed around the complexity of the GC neither (speculative, I am just guessing here)
I think that's it from my side, and I am looking forward to hear from you, TC39 members.