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

Per Liden per.liden at oracle.com
Wed Mar 23 15:07:45 UTC 2016


Hi Peter,

On 2016-03-23 15:02, Peter Levart wrote:
> 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.

I've prototyped the VM side. I've ignored the "await" issue for now as I 
first just wanted the basic structure up. I'm running out of time for 
today (and I'll be away the rest of the week) but let's continue the 
discussion next week and figure out the "await" details/alternatives.

Webrevs for jdk9/hs-rt:

http://cr.openjdk.java.net/~pliden/reference_pending_list/webrev.0-jdk
http://cr.openjdk.java.net/~pliden/reference_pending_list/webrev.0-hotspot

It passes jdk/test/java/lang/ref/* and our VM tests for reference 
processing.

cheers,
Per



More information about the core-libs-dev mailing list