JNI WeakGlobalRefs

David Holmes david.holmes at oracle.com
Wed Jul 21 22:25:06 UTC 2021


Hi Hans,

On 22/07/2021 7:54 am, Hans Boehm wrote:
> Is this an appropriate list to discuss JNI?

No - hotspot-dev (to get runtime and GC folk) is the place to discuss JNI.

Thanks,
David

> I'm concerned that the current semantics of JNI WeakGlobalRefs are still
> dangerous in a very subtle way that is hidden in the spec. The current
> (14+) spec says:
> 
> “Weak global references are related to Java phantom references
> (java.lang.ref.PhantomReference). A weak global reference to a specific
> object is treated as a phantom reference referring to that object when
> determining whether the object is phantom reachable (see java.lang.ref).
> ---> Such a weak global reference will become functionally equivalent to
> NULL at the same time as a PhantomReference referring to that same object
> would be cleared by the garbage collector. <---”
> 
> (This was the result of JDK-8220617, and is IMO a large improvement over
> the prior version, but ...)
> 
> Consider what happens if I have a WeakGlobalRef W that refers to a Java
> object A which, possibly indirectly, relies on an object F, where F is
> finalizable, i.e.
> 
> W - - -> A -----> ... -----> F
> 
> Assume that F becomes invalid once it is finalized, e.g. because the
> finalizer deallocates a native object that F relies on. This seems to be a
> very common case. We are then exposed to the following scenario:
> 
> 0) At some point, there are no longer any other references to A or F.
> 1) F is enqueued for finalization.
> 2) W is dereferenced by Thread 1, yielding a strong reference to A and
> transitively to F.
> 3) F is finalized.
> 4) Thread 1 uses A and F, accessing F, which is no longer valid.
> 5) Crash, or possibly memory corruption followed by a later crash elsewhere.
> 
> (3) and (4) actually race, so there is some synchronization effort and cost
> required to prevent F from corrupting memory. Commonly the implementer of W
> will have no idea that F even exists.
> 
> I believe that typically there is no way to prevent this scenario, unless
> the developer adding W actually knows how every class that A could possibly
> rely on, including those in the Java standard library, are implemented.
> 
> This is reminiscent of finalizer ordering issues. But it seems to be worse,
> in that there isn't even a semi-plausible workaround.
> 
> I believe all of this is exactly the reason PhantomReference.get() always
> returns null, while WeakReference provides significantly different
> semantics, and WeakReferences are enqueued when an object is enqueued for
> finalization.
> 
> The situation improves, but the problem doesn't fully disappear, in a
> hypothetical world without finalizers. It's still possible to use
> WeakGlobalRef to get a strong reference to A after a WeakReference to A has
> been cleared and enqueued. I think the problem does go away if all cleanup
> code were to use PhantomReference-based Cleaners.
> 
> AFAICT, backward-compatibility aside, the obvious solution here is to have
> WeakGlobalRefs behave like WeakReferences. My impression is that this would
> fix significantly more broken clients than it would break correct ones, so
> it is arguably still a viable option.
> 
> There is a case in which the current semantics are actually the desired
> ones, namely when implementing, say, a String intern table. In this case
> it's important the reference not be cleared even if the referent is, at
> some point, only reachable via a finalizer. But this use case again relies
> on the programmer knowing that no part of the referent is invalidated by a
> finalizer. That's a reasonable assumption for the
> Java-implementation-provided String intern table. But I'm not sure it's
> reasonable for any user-written code.
> 
> There seem to be two ways forward here:
> 
> 1) Make WeakGlobalRefs behave like WeakReferences instead of
> PhantomReferences, or
> 2) Add strong warnings to the spec that basically suggest using a strong
> GlobalRef to a WeakReference instead.
> 
> Has there been prior discussion of this? Are there reasonable use cases for
> the current semantics? Is there something else that I'm overlooking? If
> not, what's the best way forward here?
> 
> (I found some discussion from JDK-8220617, including a message I posted.
> Unfortunately, it seems to me that all of us overlooked this issue?)
> 
> Hans
> 


More information about the core-libs-dev mailing list