Analysis on JDK-8022321 java/lang/ref/OOMEInReferenceHandler.java fails intermittently

Peter Levart peter.levart at gmail.com
Wed Mar 23 14:02:55 UTC 2016


Hi Per, Kim,

On 03/22/2016 10:24 AM, Per Liden wrote:
> So, I imagine the ReferenceHandler could do something like this:
>
> while (true) {
>     // getPendingReferences() is a downcall to the VM which
>     // blocks until the pending list becomes non-empty and
>     // returns the whole list, transferring it to from VM-land
>     // to Java-land in a safe and robust way.
>     Reference<Object> pending = getPendingReferences();
>
>     // Enqueue the references
>     while (pending != null) {
>         Reference<Object> r = pending;
>         pending = r.discovered;
>         r.discovered = null;
>         ReferenceQueue<? super Object> q = r.queue;
>         if (q != ReferenceQueue.NULL) {
>             q.enqueue(r);
>         }
>     }
> } 

...so I checked what it would be needed if there was such 
getPendingReferences() native method. It turns out that a single native 
method would not be enough to support the precise direct ByteBuffer 
allocation. Here's a refactored webrev that introduces a 
getPendingReferences() method which could be turned into a native 
equivalent one day. There is another native method needed - int 
awaitEnqueuePhaseStart():

http://cr.openjdk.java.net/~plevart/jdk9-dev/removeInternalCleaner/webrev.09.part2/

The need for this additional method arises when one wants to combine 
reference discovery with enqueueing of discovered references into one 
synchronous operation (discoverAndEnqueueReferences()). A direct 
ByteBuffer allocating thread wants to trigger reference discovery 
(System.gc()) and wait for discovered references to be enqueued before 
continuing with direct memory reservation retries. An alternative to 
what I have done in above webrev would be a maintenance of a single 
enqueuePhase counter on the Java side with usage roughly as:

discoverAndEnqueueReferences() {
     int phase = Reference.getEnqueuePhase();
     System.gc();
     Reference.awaitEnqueuePhaseGreaterThan(phase);
}

But in that case, System.gc() would have to guarantee that after 
discovery of no new references, blocked getPendingReferences() would 
still return with an empty list of References (null) just to keep the 
DBB allocating thread alive. I have tried to do this variant and 
unfortunately it can't be reliably performed with current protocol as 
getPendingReferences() can only be programmed to return non-empty 
Reference lists without ambiguity. I created a DirectBufferAllocOOMETest 
to exercise situations where no new Reference(s) are discovered in a GC 
round.

So do what do you think - what would it be easier to support:
a) getPendingReferences() returns empty Reference list (null) after a GC 
round that discovers no new pending references
b) getPendingReferences() returns when new Reference(s) are discovered 
and there is an additional int awaitEnqueuePhaseStart() as defined in 
above webrev.

Regards, Peter




More information about the core-libs-dev mailing list