Why Does Thread.sleep(0) Appear in RocketMQ? Uncovering the Safepoint Trick
This article investigates the mysterious "prevent gc" comment in RocketMQ source code, explains how Thread.sleep(0) interacts with JVM safepoints and counted loops, and demonstrates practical fixes by changing loop indices or inserting explicit safepoint calls.
This article starts from a puzzling comment "prevent gc" found in RocketMQ's source code, illustrated by a for‑loop that records the iteration count and executes an if every 1,000 cycles.
The comment translates to "prevent GC thread from garbage collection" and the core line of code is:
Thread.sleep(0);
The author wonders whether this line truly prevents GC. By tracing the code to
org.apache.rocketmq.store.logfile.DefaultMappedFile#warmMappedFile, he proposes changing the loop index from int to long and removing the if logic.
Exploration
The investigation continues by searching commit logs and GitHub issues for the phrase "prevent gc". No definitive answer is found, but a StackOverflow question reveals that Thread.sleep(0) is used to give the GC thread a chance to run, effectively triggering GC rather than preventing it.
It does not… (the answer explains that the call gives GC a chance to be selected by the OS).
The key insight is that the code aims to "trigger" GC to avoid long‑lasting pauses, not to stop GC entirely.
The underlying mechanism is the JVM safepoint. According to Deep Understanding of the JVM , only when a thread reaches a safepoint can the GC pause the world. HotSpot treats loops with small integer indices as "counted loops" and omits safepoints inside them. Switching the index to long makes the loop an "uncounted loop", inserting a safepoint.
HotSpot optimizes counted loops (int index) by not placing safepoints; uncounted loops (long index) do have safepoints.
Since Thread.sleep is a native method, returning from it forces a safepoint check, effectively inserting a safepoint in the counted loop.
Practice
A runnable that executes a large counted loop is used to demonstrate the effect. Two threads run the loop while the main thread sleeps for 1 s. The main thread does not resume until the counted loops finish, showing the safepoint delay.
public class MainTest {
public static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
for (int i = 0; i < 1000000000; i++) {
num.getAndAdd(1);
}
System.out.println(Thread.currentThread().getName() + " execution finished!");
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("num = " + num);
}
}Changing the loop index to long (or inserting Thread.sleep(0)) makes the program behave as expected because the safepoint is reached during the loop.
Extra Note
The article also mentions a related snippet that pre‑warms a file by writing zeros to a 4 KB buffer:
byteBuffer.put(i, (byte) 0);
This technique appears in other performance‑critical scenarios, such as the Tianchi competition.
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
