Some evil thoughts here ... the moment there is any mechanism to hint the GC that mechanism will be abused in all possible ways.
As example, if growing a resizable array allows GC to kicks earlier than usual, I can see already ad-hoc libraries created only for that purpose that grows and shrink randomly buffers to pretend to better control the GC lifecycle, this is why instead of poking around solutions I think the best way to go is to offer any way a new primitive that can hint such GC that memory is better freed sooner than later.
That hint must never guaratee anything but internally can simulate some added pressure and either make the light-collector works sooner (if that's what we're after, as I did dig into v8 internals yesterday and it looks like there are at least 3 levels of GC if not more, cross-platform speaking) or somehow schedule, accordingly to heuristic I am not familiar with, a GC call in the nearest possible future and ignore such hint if already scheduled and not executed yet.
The way I've otherwise managed to make the GC kick ASAP is to create a never cleaned up array of thousand objects with random values that cannot be ignored and finally splice it after all my observed references in the FinalizationRegistry have been cleared ... this grows exponentially on the JS side of affairs only but at least it keeps more free the WASM related counter-part but is ugly as hell and a horrible hack nobody should even think about using it out there in production.
This is to say that if the engine doesn't provide a way, people will explore uglier ways to obtain the same result to avoid crashing, with relatively ease, constrained WASM interpreters that try to use the least amount of RAM because maybe these usually run in very constrained hardware too (MicroPython, Lua, other WASM runtimes I am dealing with these days).
I mean this is not novel. You can already do that today. I have written heuristic GC triggers that allocate filled arrays.
Yup exactly this.
What I'm trying to capture is something close to the actual usage. Since there are no guarantees the finalization callbacks will run timely, the linear memory manager must be able to grow its memory (or accept to run out). In that sense allocating more linear memory is a signal. Providing an earlier signal would be better, but I'm not sure what shape that could have.
To be honest I think for these non web environments, it's ok to rely on not fully specified behavior, e.g. the host would expose GC triggers in a non-standard way. While not wasm related, we have similar requirements to track garbage collection (to implement a poor man's distributed GC), and we do heavily use the gc global that a lot of hosts implement (but that is often not exposed by default). We also rely on the fact that finalization callbacks are implemented to all run after the promise queue has drained, but before the next set immediate, in the embedded engine we care about.
If some use cases show that cleanupSome should be re-introduced, e.g. to ensure all finalizers can be forced run synchronously after a forced gc, then we can revive that proposal.
to me that could be a static method to the static global class everyone uses: FinalizationRegistry.hintGC()
That would do something to tell GC to schedule an update ASAP, it will ignore repeated calls to that call while the scheduled operation hasn't run yet, and it might be good enough for WASM or other scenarios/use-cases where earlier GC calls are desired from the cient side, so that WASM engines can trigger that based on their memory threshold, but server-driven "holding references" can also signal each client it's time to free more memory.
In a previously mentioned mainframe scenario, where many clients connect to the server and can somehow allocate references in there, and my coincident library is just capable of doing that, which target is usually something like a RaspberryPi and its contrains, this hint could also be triggered or propagated per each client on demand, because if clients have different hardware but the memory related part is distributed, the first time somebody with 128GB of RAM and a deadly fast machine enters the room the server, and every other client, could die before that machine releases its held references that ar enot needed anymore.
Specially because I've read in V8 that GC is platform subjective/dependent, it can be incremental or not, I am really concerned about the fact any client could dictate the memory life of any distributed solution, which is, in my opinoin, a bummer, if there's no way such distributed architecture can at least hint clients to be a bit more greedy about their memory clean-up.
Back to reality and real-world use cases, I am dealing with the requirement of a single worker that is able to handle, understand, hold, and forward back, same references (within the worker) to multiple clients (different scripts on the page or even in other workers) so that having that worker die because all clients never freed early enough their cross-realm references is a problem.
That is too imperative and global, and as such very unlikely to reach consensus. That's why I was trying to figure out something more localized and less "do as I say", but more "look at what I do".
Yeah that's the biggest issue. Distributed GC is by definition cooperative. When an environment exports a reference to others, it assumes the risk that these references may be held forever. There is some research around this, but basically it mostly boils down to either trusting the other environments to behave, having a mechanism to disable misbehaving peers, or have some kind of economic incentive for each environment to behave (e.g. charge a fee to every environment holding your references).
Back to the GC hint question, if multiple environments are involved (whether workers or network), I agree that you need a signaling mechanism that can represent the memory pressure of other environments. All trivial approaches basically look imperative in that case (with the memory tracing approach I described in a previous post, there might be more localized memory pressure hints possible, but I know it will be a huge uphill battle to get any of that in the language given how niche these issues are).
My hope has always been that in browsers, there would be a "clonable" value that maintains its identity when round tripped over postMessage. Shared struct would be one of those things, but there may be others. That effectively forces the engine to implement an agent cluster wide GC, and consider how references in one agent cause memory usage in other linked agents. Then we're back at the problem of representing the cost of linear memory behind a given JS object (but still don't have good representation of the reference graph through linear memory)
I'm actually down with the postMessage(data, leaking) idea, but the API looks somehow doomed behind its arity and as the leaking reference is actually non existent in the sender (it's likely a Proxy to a remote identifier that hold memory for the client) but fully blown in the receiver, I am not sure that would ever work.
edit I guess you meant the other way around but in cross realm / client/server cases, postMessage is not necessarily a thing, websockets or HTTP requests are, so it's hard from the server to tell clients it's time to release, but I see yur point for the specific Worker case .. which would be at least something, but half-backed for the bigger, or broader, picture.
What's wrong with a too imperative approach that grants nothing so it's inherently not really imperative? It's a hint ... as in "could you please run for a minute, whenever you have time?" ... not an order, rather a polite hint to such order.
There are other cases in software about imperative calls that might do nothing, flush() could be one, where nothing else is meant to be flushed, and generators .next() where once it's the end, it makes literally no sense whatsoever.
function* fine() { yield 'fine'; }
const be = fine();
be.next();
be.next();
// I've said ...
be.next();
No error triggered ... it's not that such invoke can change the status of the affairs ... in my idea it's just a hint, with an "already shceduled" guard behind: what's so bad?
All frameworks aiming to perform best won't ever care about that API, all other actually more relevant use cases where greedier GC is needed might use such API on demand ... how bad can that be?