[concurrency-interest] Is Reference.reachabilityFence() needed in Reference constructor?

Vitaly Davidovich vitalyd at gmail.com
Thu Oct 22 11:35:58 UTC 2015


It's unlikely there would be a safepoint between those two statements in
compiled code.  Polls are typically placed at method exit (modulo polls
inserted for some types of loops, but that's irrelevant to this instance).
With inlining, the poll distance is likely to increase as well.  However,
there's nothing stopping compiler from scheduling these two statements
sufficiently apart, particularly since there's no data or control flow
dependence between them, and ending up with a safepoint in between.  It's
unlikely probably, but possible.  The bottom line is this code needs manual
lifetime extension facility.

sent from my phone
On Oct 22, 2015 7:22 AM, "Peter Levart" <peter.levart at gmail.com> wrote:

> Hi,
>
> Thanks for the insight.
>
> I'd like to note that I'm not concerned about the reachability of the
> Reference object itself (program code takes care of that - for example in
> Cleaner API it hooks Reference objects into a doubly-linked list exactly
> for them to stay reachable until discovered by GC and enqueued by
> ReferenceHandler thread into the associated ReferenceQueue). I'm merely
> interested in the reachability of the referent until the Reference (and any
> possible Reference subclass constructor) fully constructs the Reference
> instance.
>
> The situation where this would manifest as a malfunction is improbable (as
> the referent is usually passed to other parts of code too and is reachable
> from elsewhere for some time). But in case it isn't, the following is
> possible:
>
>     Reference(T referent, ReferenceQueue<? super T> queue) {
>         this.referent = referent;
>         // - safepoint with GC happens, 'referent' is found
> weakly-reachable, 'this' Reference is hooked on the pending chain
>         // - ReferenceHandler thread unhooks 'this' from pending chain and
> tries to enqueue it, but this.queue is still null
>         // - BANG! NPE in Referencehandler thread which terminates it!
>         this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
>     }
>
> A pre-requisite for above scenario is a safepoint between "this.referent =
> referent" and "this.queue = ...". In interpreter, safepoint check is after
> every bytecode instruction right? But in Interpreter, I think arguments are
> also kept on the stack and are therefore reachable at least until the end
> of constructor. How probable is a safepoint between those two statements in
> JIT-ed code? Maybe the Reference constructor itself is not in danger
> because it is very trivial, but what about subclasses that must do their
> own part of initialization too (for example Cleaner API discussed on
> core-libs-dev)?
>
> Regards, Peter
>
> On 10/22/2015 12:25 AM, Vitaly Davidovich wrote:
>
> Yes, if runtime stripes that instance before it is exposed anywhere
>> (e.g. scalarizes the weakref)
>
>
> If you look at escape analysis code (
> http://hg.openjdk.java.net/jdk9/hs-comp/hotspot/file/a60bd3d34158/src/share/vm/opto/escape.cpp#l803),
> Reference and subclasses get special treatment and marked as GlobalEscape.
>
> On Wed, Oct 21, 2015 at 1:08 PM, Aleksey Shipilev <
> aleksey.shipilev at oracle.com> wrote:
>
>> On 10/21/2015 06:53 PM, Andrew Haley wrote:
>> > On 10/21/2015 04:44 PM, Aleksey Shipilev wrote:
>> >> Of course, this does not tell another (scary) part of the story, what
>> if
>> >> the Reference itself is not discovered as strong ref by GC, e.g. when
>> it
>> >> isn't published on heap, or scalarized by compiler, etc.
>> >> reachabilityFence, as currently implemented, extends the "liveness"
>> >> scope of the local variable, but does not convey anything special to
>> the
>> >> GC/runtime otherwise.
>> >
>> > If the Reference itself is not reachable from a strong root then the
>> > Reference is dead so we don't care what happens to it.  But if the
>> > Reference is put on a reachable ReferenceQueue, then it's fine.
>>
>> Yes, if runtime stripes that instance before it is exposed anywhere
>> (e.g. scalarizes the weakref), or those references are dead (e.g. the
>> weakref is buried somewhere deep in garbage subgraph, and purged on
>> sweep), then everything goes awry.
>>
>> But the thing is, a WeakReference is put on ReferenceQueue by the GC
>> itself. In the object graph, ReferenceQueue does not normally reference
>> weakrefs back. Before enqueueing the reference GC has to first discover
>> that WeakReference -- but from where? In other words, "registering" on a
>> reachable RefQueue does not make weakref to be reachable.
>>
>> Case in point:
>>
>> public class WhereIsWaldo {
>>
>>     public static void main(String... args) throws Exception {
>>         for (int c = 0; true; c++) {
>>             new WhereIsWaldo().work(c);
>>         }
>>     }
>>
>>     final ReferenceQueue<Object> rq = new ReferenceQueue<>();
>>
>>     void work(int id) throws Exception {
>>         hideWaldo(id);
>>         findWaldo(id);
>>     }
>>
>>     WeakReference<Object> gwr;
>>
>>     void hideWaldo(int id) {
>>         // Does not work, d'uh
>>         new WeakReference<>(new Object(), rq);
>>
>>         // Does not work either :(
>>         WeakReference<Object> wr =
>>            new WeakReference<>(new Object(), rq);
>>
>>         // This also does not help... once you leave the method,
>>         // the weakref is gone.
>>         Reference.reachabilityFence(wr);
>>
>>         // This one helps, makes Waldo reachable
>>         // gwr = wr;
>>     }
>>
>>     void findWaldo(int id) throws Exception {
>>         while (Thread.currentThread().isAlive()) {
>>             System.gc();
>>             Reference ref = rq.remove(1000);
>>             if (ref != null) {
>>                 return;
>>             } else {
>>                 System.out.println("Where's Waldo #" + id + "?");
>>             }
>>         }
>>     }
>> }
>>
>>
>> Thanks,
>> -Aleksey
>>
>>
>
>



More information about the core-libs-dev mailing list