This is a non-trivial effect related to HotSpot Safepoint mechanism.
Background
Usually HotSpot JVM adds a safepoint poll inside loops to allow pausing a thread when the JVM needs to execute a stop-the-world operation. The safepoint poll is not free (i.e. it has some performance overhead), so the JIT compiler attempts to eliminate it when possible. One of such optimizations is to remove a safepoint poll from counted loops.
for (int i = 0; i < 1000000000; i++)
is a typical counted loop: it has a monotonic integer loop variable (counter) and the finite number of iterations. JDK 8 JIT compiles such loop without a safepoint poll. However, this is a really long loop; it takes several seconds to complete. While this loop is running, JVM will not be able to stop the thread.
HotSpot JVM uses safepoints not only for GC, but for many other operations. In particular, it stops Java threads periodically when there are cleanup tasks to do. The period is controlled by -XX:GuaranteedSafepointInterval
option which defaults to 1000 ms.
What happens in your example
- You start two long non-interruptible loops (with no safepoint checks inside).
- The main thread goes to sleep for 1 second.
- After 1000 ms (GuaranteedSafepointInterval) the JVM attempts to stop Java threads at a safepoint for a periodic cleanup, but can't do it until the counted loops finish.
Thread.sleep
method returns from native, discovers that the safepoint operation is in progress, and suspends until the operation ends.
At this point the main thread is waiting for the loops to complete - exactly what you observe.
When you change sleep duration to 100 ms, the guaranteed safepoint happens after Thread.sleep
returns, so the method is not blocked.
Alternatively, if you leave 1000 ms sleep, but increase -XX:GuaranteedSafepointInterval=2000
, the main thread will not have to wait either.
The fix
-XX:+UseCountedLoopSafepoints
option turns off the optimization that eliminates safepoint polling. In this case, Thread.sleep
will sleep for 1 second as expected.
Also, if you change int i
to long i
, the loop will not be treated as counted anymore, so you won't see the mentioned safepoint effect.
Since JDK 10, HotSpot implemented Loop Strip Mining optimization that solves the problem of safepoint polling in counted loops without much overhead. So, your example should work fine out of the box in JDK 10 and later.
The good explanation of the problem and the solution can be found in the description of this issue.