Why Does Thread.sleep(0) Appear in RocketMQ? Uncovering the Safepoint Trick
This article investigates the mysterious Thread.sleep(0) call found in RocketMQ source, explaining how it interacts with JVM safepoints, why it appears in counted loops, and how changing loop indices from int to long can avoid long stop‑the‑world pauses, backed by code examples and references.
Exploration
The article starts with a puzzling comment in RocketMQ source that mentions prevent gc . The author examines the surrounding for loop, noting a variable j that records iteration count and an if block executed every 1000 iterations, annotated with the comment.
The core line of code is:
Thread.sleep(0);
The author wonders whether this line truly prevents garbage collection. By tracing the code origin, it is found in
org.apache.rocketmq.store.logfile.DefaultMappedFile#warmMappedFile. The author proposes changing the loop index type from int to long to remove the if logic.
Further investigation reveals that the real purpose is related to JVM safepoints . A safepoint is required before the GC can pause the world. HotSpot treats loops that use int as counted loops and may omit safepoint checks until the loop finishes, causing long pauses.
By converting the loop variable to long, the loop becomes an uncounted loop , which forces the JVM to insert a safepoint inside the loop, allowing GC to run without waiting for loop termination.
The author validates this hypothesis with a small Java program that runs two long for loops in parallel and sleeps the main thread. When using int, the main thread blocks until the loops finish; after changing to long, the program proceeds as expected.
Additional images illustrate the code snippets, the safepoint documentation, and the experimental results.
Practice
The author provides a runnable example:
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);
}
}Running this code demonstrates the pause caused by counted loops. Changing the loop variable to long eliminates the issue.
Additional Note
The article also touches on a related technique of file pre‑warming by writing zeros to a ByteBuffer in 4 KB chunks, linking to a competition where participants used similar ideas.
Overall, the piece explains why Thread.sleep(0) is used to trigger a safepoint, how counted loops affect GC pauses, and how a simple type change can improve performance.
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.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
