Fundamentals 12 min read

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.

dbaplus Community
dbaplus Community
dbaplus Community
Why Thread.sleep(0) Can Influence GC: Uncovering JVM Safepoint Tricks

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#warmMappedFile

contains 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

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaJVMSafepointRocketMQgcthread-sleep
dbaplus Community
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.