Explanation
What just happened
main allocated fast, filling G1’s young generation. When the next new byte[1024] didn’t fit, main itself asked for a young collection. The VM Thread brought every Java thread to a safepoint, so main and ticker both stopped. GC worker threads then copied the live objects out of the young generation in parallel, and everyone resumed.
ticker never allocated. It didn’t cause the pause, but it waited for it all the same.
Why it works like this
Copying live objects while other threads change them would be unsafe, so G1 stops every Java thread for the young collection.
Watch out
A pause stops every Java thread, so even code that allocates nothing waits for it. JFR records each one: a jdk.GarbageCollection event named G1New, with cause G1 Evacuation Pause.
Common questions
What triggers a young GC in Java?
An allocation that doesn't fit. The thread whose allocation fails asks the JVM for a young collection, and in this program that is main.
Does a young GC stop all threads?
Yes. G1 runs it at a safepoint on the VM Thread, and every Java thread is stopped until it ends, even ones that never allocate.
Which threads do the garbage collection work in Java?
GC worker threads, named GC Thread#0, GC Thread#1 and so on, copy the live objects in parallel. Your own threads, like main and ticker here, never do that work.
Verified: this timeline matches a real program, checked in CI on JDK 21 and 25. How we verify. Unclear or wrong? Tell us