Why Thread.sleep(0) Can Influence GC: Uncovering JVM Safepoint Tricks
This article explores the puzzling "prevent gc" comment in RocketMQ source code, explains how Thread.sleep(0) interacts with JVM safepoints, distinguishes counted and uncounted loops, and demonstrates practical experiments that reveal why altering loop variables can change GC behavior.
Background
In the RocketMQ source code a comment “prevent gc” appears next to a call Thread.sleep(0). The investigation aims to understand why a zero‑millisecond sleep is used.
JVM safepoints
HotSpot stops the world only at safepoints. Loops whose index variable is of type int (or other small integral types) are treated as “counted loops”. The JIT omits safepoint polls inside such loops, so a long‑running counted loop can block garbage‑collection until the loop finishes. Loops that use long indices are “uncountable loops” and contain safepoint checks on each iteration.
Implication for the RocketMQ code
The method
org.apache.rocketmq.store.logfile.DefaultMappedFile#warmMappedFilecontains a loop that iterates with an int counter and, every 1000 iterations, executes Thread.sleep(0) with the comment “prevent gc”. The sleep call forces the thread to enter native code, which triggers a safepoint poll, allowing the GC thread to run.
Proposed simplification
Replace the int loop variable with long and remove the conditional if (i % 1000 == 0) Thread.sleep(0). The long index automatically inserts safepoint checks, achieving the same effect without extra code.
Demonstration
public class MainTest {
public static java.util.concurrent.atomic.AtomicInteger num = new java.util.concurrent.atomic.AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable r = () -> {
for (int i = 0; i < 1_000_000_000; i++) {
num.getAndAdd(1);
}
System.out.println(Thread.currentThread().getName() + " execution finished!");
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("num = " + num);
}
}Both threads run a counted loop; the main thread blocks until the loops finish because no safepoint is reached. Changing the loop index to long makes the program continue after the 1‑second sleep, confirming that the long type forces safepoint polls.
Conclusion
Thread.sleep(0)is not meant to “prevent GC” but to create a safepoint opportunity, reducing the duration of stop‑the‑world pauses. Converting counted loops to uncounted loops (e.g., using long) is a cleaner way to achieve the same effect.
References
RocketMQ issue: https://github.com/apache/rocketmq/issues/4902
StackOverflow discussion: https://stackoverflow.com/questions/53284031/why-thread-sleep0-can-prevent-gc-in-rocketmq
OpenJDK safepoint source: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/tip/src/share/vm/runtime/safepoint.cpp
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
dbaplus Community
Enterprise-level professional community for Database, BigData, and AIOps. Daily original articles, weekly online tech talks, monthly offline salons, and quarterly XCOPS&DAMS conferences—delivered by industry experts.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
