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