LossyRef, better than WeakRef

In How to implement TwoWayWeakMap? Using WeakRef? there is an example of a TwoWayWeakMap.

However a problem is that, if someone tries to get an object key using a non-object value with the keyFrom method, if they do this often enough before GC ever has a chance to kick in, then it is possible they can inadvertently prevent the object from being collected (depending on the runtime, and depending on random timing of a program).

It seems that a new feature like LossyRef, whose deref() method would return undefined if an object is no longer reachable (except for in the LossyRef itself), would lead to more robust and fail-proof code.

As soon as a variable goes out of scope and is no longer reachable by any other code (the ref inside LossyRef not counting), then lossyRef.deref() would return undefined, although the object may still have not been collected (but will be).

Bike shed naming ideas: LossyRef, ReachableRef, ScopedRef

That's easier said than done. If you knew the point at which an object becomes unreachable, then you wouldn't need a garbage collector, and consequently LossyRef would be equivalent to WeakRef.

3 Likes

Polling a WeakRef is an anti-pattern. FinalizationRegistry callbacks are usually what should be used instead to avoid observing the object and keeping it alive.

As @lightmare mentioned, an object becoming unreachable is exactly what WeakRef and FinalizationRegistry handle, there is no other step.

@mhofman WeakRef returns an object if it hasn't been collected yet, even if there are no references to it. This means that user code can accidentally regain a reference to an object that would otherwise soon be collected.

Use of FinalizationRegistry does not prevent this issue in any way; FinalizationRegistry only notifies us after an object has been collected, but before this happens, a weakref.deref() call can still cause code to go from zero strong references of an object to more than zero strong references of the object, therefore this can prevent a FinalizationRegistry callback from ever firing.

The idea here is that LosableRef (toying with naming ideas) entirely prevents this problem.

@lightmare It seems possible for a JavaScript engine to track when objects go out of scope and are no longer referenced. I don't think this means an object has to be immediately collected as soon as it is no longer referenced.

However, I'm not knowledgeable enough to know whether this would add too much overhead.

What I do know, is that this concept would prevent the above issue.

Is it possible to do it, and do it performantly enough?

(If you can explain why it isn't possible, that would be super helpful.)

An object reference going out of scope and an object no longer having any references by the program are 2 different things. Engines generally implement generational gc, which doesn't track single references but instead check if any reference still exist.

Basically what you're asking is to gain some knowledge about the references that exist to your object that the engine doesn't have. Garbage collection is how an engine finds out an object is unreachable by the program and can be collected. Usually the object will be collected immediately during garbage collection. The weakref target and the weak collection entries will be cleared right away, then at some point later the finalization registry callback will be invoked.

Derefing a weakref during that window will not rematerialize the object. And before garbage collection, the engine has no other state regarding the references to the object, it's like if the program still held a reference somewhere.

FYI, I did implement your TwoWayWeakMap avoiding unnecessary deref, which should greatly reduce this weakref "reanimation" case, and limits it to when the program gets the keys associated to a value in the map.

2 Likes

To add to what @mhofman said. Some garbage collectors are also conservative.

Which means they can mistake some other data as a reference to the object. So even when there are no real references left an object may still not be collected after a GC scan.

1 Like