SoV-2: weak references
Dan Heidinga
heidinga at redhat.com
Wed Jan 19 21:28:50 UTC 2022
In the Dec 2021 update to State of Valhalla, part 2: The language
model, the section on "Identity-sensitive operations" states this
about weak references:
> Weak references to value objects that contain no references to identity objects should never be
> cleared; weak references to value objects that contain references to identity objects should be
> cleared when those objects are no longer strongly reachable.
I'm concerned that this approach makes using weak references correctly
for values very difficult, especially in the face of scalarization.
Scalarization allows separating values into their individual
components and flowing them separately through the code. This can -
with some caveats for decompilation and maybe other features - result
in very different lifetimes for each separate component, with some
object references going out of scope "earlier" than expected. The
source code will of course show the *entire* value being carried along
even though only a single component may survive post-scalarization
which means developers will see very different behaviour between the
interpreter (which doesn't scalarize) and jitted code - making it very
easy to write tests that give them the wrong impression.
Reference::reachabilityFence was added in Java 9 to help users deal
with premature finalization and has gotten little attention from most
users. The proposed semantics for WeakReferences on value objects will
drag the reachabilityFence method into the light and expose users to a
problem they'd rather not have. Just as we avoided exposing most
users to tearing of their (bucket 2) value objects, we shouldn't
expose them to having to reason about complicated reachability rules.
Which is to say, Reference objects and value objects need to be
incompatible to give users what they expect. Anything else is loading
a gun and pointing it at their feet.
Here's an example of why the suggested rules for weak references using
the identity objects may bring reachability issues to the forefront
(where we don't want them):
public value class ResourcePair {
static long nativePtr;
static IdentityObject companion;
ResourcePair(long ptr, IdentityObject o) {
nativePtr = ptr;
companion = o;
}
}
class PtrRef extends WeakReference<ResourcePair> {
long nativePtr;
PtrRef(ResourcePair p, ReferenceQueue q) {
super(p, q);
nativePtr = p.nativePtr;
}
void free() { /* do something to nativePtr */ }
}
If the `companion` object of the ResourcePair value goes out of scope
before `nativePtr` is done being used - the issue of reachabilityFence
- the ReferenceQueue processor could call free() before the
`nativePtr` is done being used. Scalarization will make this more
common and even very common if we're getting the benefits we expect
from it.
--Dan
More information about the valhalla-spec-experts
mailing list