Integrated: 8371200: ZGC: C2 allocation deopt race
Erik Österlund
eosterlund at openjdk.org
Thu Nov 20 14:37:31 UTC 2025
On Thu, 6 Nov 2025 11:35:50 GMT, Erik Österlund <eosterlund at openjdk.org> wrote:
> C2 compiled code enjoys initializing newly allocated objects with zeros and eliding write barriers. This is good for performance, but is only really allowed for objects in the young generation. Objects in the old generation need appropriate barriers and coloring null pointers when using ZGC - otherwise remembered set updates will be lost and the JVM will eventually crash.
>
> When the JIT calls into the runtime in the slow path of an allocation, we will allocate an object initially in the young generation. However, when transitioning from vm back to Java on the way back to the JIT compiled code, the GC can make unexpected progress, causing the just allocated object to age unexpectedly.
>
> ZGC currently has guards that try to detect objects that relocated to the old generation, as well as detecting flip promotions where a ZPage flips from young to old in the page table. This happens when pages are too dense to warrant relocation, or when the objects are too large to relocate.
>
> Such flip promotion happens between mark end and relocate start. ZGC has guards trying to detect that by looking if the current GC phase is between those points for the young generation, and if the page is a candidate for promotion.
>
> However, whether a page is a candidate for promotion is subject to the tenuring threshold, which is calculated in the same phase. Therefore, there exists a theoretical race that may occur if the guarding code observes a higher tenuring threshold and it then gets lowered, causing a racy flip promotion of a page. Racy flip promotion can cause promotion barriers that wash away uncolored null pointers to race with JIT-compiled code that puts raw nulls back into the object. This will cause reference locations to not have bad bits when the relocate start GC pause comes, which means that after relocation, store barriers will not trap to register the first mutation of a reference field in the old generation, causing remembered set entries to be lost.
>
> This patch proposes a more controlled race. We first flip age pages, then handshake, and then execute the promotion barriers that purge raw null pointers from objects being promoted. This way, the guarding code that detects unfortunately aged objects only needs to check if the object returned is old, and then deopt the frame to the interpreter which will run the required barriers and not cheat.
>
> If the guard check is performed before the handshake, then the JIT-compiled code will continue to execute past the code that as...
This pull request has now been integrated.
Changeset: b9ee9541
Author: Erik Österlund <eosterlund at openjdk.org>
URL: https://git.openjdk.org/jdk/commit/b9ee9541cffb6c5a737b08a69ae04472b3bcab3e
Stats: 104 lines in 5 files changed: 52 ins; 44 del; 8 mod
8371200: ZGC: C2 allocation deopt race
Reviewed-by: aboldtch, stefank
-------------
PR: https://git.openjdk.org/jdk/pull/28176
More information about the hotspot-gc-dev
mailing list