DirectBufferAllocTest fails after replacement of Thread.sleep() with Thread.onSpinWait()

David Holmes david.holmes at oracle.com
Mon Jun 20 21:58:47 UTC 2022


On 21/06/2022 12:49 am, Сергей Цыпанов wrote:
> 
>> Do you get the OOME using Thread.yield() or just a busy-wait?
> 
> I reproduce it with busy-wait involving Thread.onSpinWait() and only when the test is run with -XX:MaxDirectMemorySize=128m -XX:-ExplicitGCInvokesConcurrent
> 
> If I either drop mentioned VM options or use Thread.sleep() OOME will never happen (at least on my machine).

That indicates that without those flags, or with the sleep, memory use 
does not outpace memory return.

> P.S. If I use Thread.yield() the problem is not reproduced any more.

Which indicates yielding is having a similar effect to sleeping - though 
I would expect it to be less pronounced.

> P.P.S. How should I interpret this? Thread.onSpinWait() is discouraged for busy-waiting?

Thread.onSpinWait _is_ busy-waiting, but it may (depending on 
architecture) actually execute a more (power) efficient form of busy-wait.

The takeaway from this exercise is that loops with sleeps can't always 
be replaced by loops that busy-wait. Functionally the logic is the same, 
but you can't just ignore the concurrency aspects.

Cheers,
David
-----

>> On 20/06/2022 7:43 pm, Сергей Цыпанов wrote:
>>
>>> Hello,
>>>
>>> while playing with Thread.onSpinWait() in busy-wait loops I've replaced Thread.sleep() with
>>> preferable (at least as it appears from the JavaDoc) Thread.onSpinWait() in java.nio.Bits.
>>>
>>> Immediately after that DirectBufferAllocTest started to fail with OOME. This is surprising, as the change
>>> does not seem to change any program logic:
>>
>> Do you get the OOME using Thread.yield() or just a busy-wait? The sleep
>> limits the computation and ensures other threads can make progress;
>> whereas a busy loop may not allow that and so you exhaust memory.
>>
>> David
>>
>>> diff --git a/src/java.base/share/classes/java/nio/Bits.java b/src/java.base/share/classes/java/nio/Bits.java
>>> --- a/src/java.base/share/classes/java/nio/Bits.java (revision 7d4df6a83f6333e0e73686b807ee5d4b0ac10cd2)
>>> +++ b/src/java.base/share/classes/java/nio/Bits.java (date 1655709575452)
>>> @@ -162,8 +162,7 @@
>>> }
>>> try {
>>> if (!jlra.waitForReferenceProcessing()) {
>>> - Thread.sleep(sleepTime);
>>> - sleepTime <<= 1;
>>> + Thread.onSpinWait();
>>> sleeps++;
>>> }
>>> } catch (InterruptedException e) {
>>>
>>> Another surprise here is that when I copy the test as simple Java application
>>> it fails when I run it with the same JVM options (-XX:MaxDirectMemorySize=128m -XX:-ExplicitGCInvokesConcurrent),
>>> but doesn't if I put a breakpoint in IntelliJ at sleeps++ and stop there for 6-7 times while
>>> the test is running. No failure observed if I drop JVM options either.
>>>
>>> Is it a bug somewhere in GC/runtime or am I doing something wrong?
>>>
>>> Regards,
>>> Sergey Tsypanov


More information about the core-libs-dev mailing list