Why Does Thread.sleep(0) Prevent GC? Uncovering JVM Safepoint Tricks
This article examines a puzzling 'prevent gc' comment in RocketMQ source, explains how inserting Thread.sleep(0) creates a JVM safepoint to avoid long GC pauses, explores counted vs uncounted loops, and demonstrates practical code modifications to improve performance.
The author starts from a strange comment in RocketMQ source code that reads "prevent gc" and wonders why a single line Thread.sleep(0); could achieve that.
The comment actually means "prevent GC thread from running garbage collection". The core logic is just that one line of code.
Thread.sleep(0);
The author discovers the code belongs to
org.apache.rocketmq.store.logfile.DefaultMappedFile#warmMappedFile. Although the comment is misleading, the technique is independent of RocketMQ and can be applied elsewhere.
By changing the loop index type from int to long, the surrounding if logic can be removed, turning a counted loop into an uncounted loop.
Exploration
Searching the commit history and GitHub issues yields no clear answer from the original author. A Stack Overflow question (ID 53284031) discusses the same issue, but its sole answer is unsatisfactory.
The answer claims that calling Thread.sleep(0) gives the GC thread a chance to be scheduled, potentially increasing GC frequency but preventing long pauses.
In reality, the code aims to "trigger" GC at safe points rather than avoid GC entirely. Java programmers usually rely on the JVM to manage GC automatically.
The key concept is the JVM safepoint. According to Understanding the JVM (3rd edition) , a thread can only be paused for GC at a safepoint.
"With safepoints, the JVM forces the program to stop only at designated points, not at arbitrary instruction locations."
HotSpot treats loops with small index types ( int) as counted loops and may omit safepoints inside them. Switching to long makes the loop an uncounted loop , which includes safepoints.
This optimization explains why the original code inserts Thread.sleep(0): the native sleep call forces the thread to enter a safepoint, allowing the GC thread to run without waiting for the loop to finish.
Practice
A sample program demonstrates the effect:
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);
}
}When the loop uses int, the main thread blocks at Thread.sleep(1000) until the two long-running loops finish, because the GC cannot reach a safepoint. Changing the loop index to long (or inserting Thread.sleep(0)) allows the GC to intervene earlier and the program proceeds as expected.
Performance tests on the author's machine show negligible time differences, but the prevent gc trick is considered a sophisticated, low‑level optimization.
Additional Thoughts
The article also mentions a related method that pre‑warms files by writing zeros to a 4 KB buffer, a technique used in a 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.
