JEP 423: Region Pinning for G1

JEP 423 (Region Pinning for G1) changes how the JVM handles JNI critical sections. Previously, when a thread entered native code that held a critical reference to Java objects, the JVM had to block moving (compacting) GC globally, which could delay garbage collection and lead to allocation failures. With JEP 423, the JVM pins only the heap regions containing those objects, instead of stopping GC for the entire heap. This allows GC to continue reclaiming memory in other regions, preventing artificial OOMs caused by global GC blocking.

Jira Case

Problem when native memory is allocated: https://support.atlassian.com/jira/kb/jira-running-out-of-memory-due-to-gc-allocation-race-condition/arrow-up-right

Before JEP 423 (old behavior)

When a thread entered a JNI critical section (for example via GetPrimitiveArrayCritical):

  • The JVM activated the GC locker

  • Certain GC phases (especially compacting / moving GC) were globally blocked

  • While GC was blocked:

    • Other threads kept allocating

    • Heap occupancy rose

    • Native + heap pressure accumulated

  • Eventually:

    • Allocation failed

    • JVM threw OOM or was killed

    • Even though a GC could have freed memory

So the failure mode was:

GC was paused globally because of native pinning, but allocation continued globally.

That mismatch is the real bug.

After JEP 423 (Region Pinning in G1)

Instead of:

“GC must stop globally while native code holds references”

The JVM now does:

“Only pin the regions that are referenced by native code”

Meaning:

  • Objects referenced by JNI critical sections:

    • are not moved

    • live in pinned regions

  • GC can still:

    • reclaim garbage in other regions

    • keep allocation pressure under control

  • No global GC lock

  • No allocation starvation

Crucially:

GC is no longer globally paused — only locally constrained.

Links:

Last updated