Under the hood JVM: Safepoints

aarti gupta
software under the hood
5 min readDec 31, 2017

--

  • A safepoint is a range of execution where the state of the executing thread is well described. Safepoints are a common JVM implementation detail
  • At a safepoint the mutator thread is at a known and well defined point in it’s interaction with the heap. This means that all the references on the stack are mapped (at known locations) and the JVM can account for all of them. As long as the thread remains at a safepoint we can safely manipulate the heap + stack such that the thread’s view of the world remains consistent when it leaves the safepoint.
  • All current JVMs have some requirement for global safepoints
  • A Java thread is at a safepoint if it is blocked on a lock or synchronized block, waiting on a monitor, parked, or blocked on blocking IO. Essentially these all qualify as orderly de-scheduling events for the Java thread and as part of tidying up before put on hold the thread is brought to a safepoint.
  • A Java thread is at a safepoint while executing JNI code. Before crossing the native call boundary the stack is left in a consistent state before handing off to the native code. This means that the thread can still run while at a safepoint.
  • A Java thread which is executing bytecode is NOT at a safepoint (or at least the JVM cannot assume that it is at a safepoint).
  • A Java thread which is interrupted (by the OS) while not at a safepoint is not brought to a safepoint before being de-scheduled.
  • The JVM and your running Java threads have the following relationship around safepoints:
  • The JVM cannot force any thread into a safepoint state.
  • The JVM can stop threads from leaving a safepoint state.

So how can the JVM bring all threads into a safepoint state? The problem is suspending a thread at a known state, not just interrupting it. To achieve this goal JVMs have the Java threads suspend themselves at convenient spots if they observe a ‘safepoint flag’.

Bringing a Java Thread to a Safepoint

Java threads poll a ‘safepoint flag’ (global or thread level) at ‘reasonable’ intervals and transition into a safepoint state (thread is blocked at a safepoint) when they observe a ‘Go to safepoint’ flag. This is simple enough, but since we don’t want to spend all of our time checking if we need to stop the C1/C2 compilers (-client/-server JIT compilers) try and keep safepoint polls to a minimum. On top of the cost of the flag check itself, maintaining a ‘known state’ adds significant complexity to the implementation of certain optimizations and so keeping safepoints further apart opens up a wider scope for optimization. These considerations combined lead to the following locations for safepoint polls:

  • Between any 2 bytecodes while running in the interpreter (effectively)
  • On ‘non-counted’ loop back edge in C1/C2 compiled code
  • Method entry/exit (entry for Zing, exit for OpenJDK) in C1/C2 compiled code. Note that the compiler will remove these safepoint polls when methods are inlined.

If you are the sort of person who looks at assembly for fun (or profit, or both) you’ll find safepoint polls in the -XX:+PrintAssembly output by looking for:

  • ‘{poll}’ or ‘{poll return}’ on OpenJDK, this will be in the instructions comments

From the mechanical sympathy mailing thread

1. A thread can be at a safepoint or not be at a safepoint. When at a safepoint, the thread’s representation of it’s Java machine state is well described, and can be safely manipulated and observed by other threads in the JVM. When not at a safepoint, the thread’s representation of the java machine state will NOT be manipulated by other threads in the JVM. [Note that other threads do not manipulate a thread’s actual logical machine state, just it’s representation of that state. A simple example of changing the representation of machine state is changing the virtual addresss that a java reference stack variable points to as a result of relocating that object. The logical state of the reference variable is not affected by this change, as the reference still refers to the same object, and two references variable referring to the same object will still be logically equal to each other even if they temporarily point to different virtual addresses].

2. “Being at a safepoint” does not mean “being blocked” (e.g. JNI code runs at a safepoint), but “being blocked” always happens at a safepoint.

3. The JVM may choose to reach a global safepoint (aka Stop-The-World), where all threads are at a safepoint and can’t leave the safe point until the JVM decides to let it do so. This is useful for doing all sorts of work (like certain GC operations, deoptimization during class loading, etc.) that require ALL threads to be at a well described state.

4. Some JVMs can bring individual threads to safepoint without requiring a global safepoint. E.g. Zing uses the term Checkpoint (first published in [1]) to describe a JVM mechanism that individually passes threads through thread-specidfic safepoints to perform certain very short operations on individual thread state without requiring a Stop-The-Wolrd pause.

5. When you write Unsafe java code, you must assume that a safepoint MAY occur between any two bytecodes.

6. Unsafe calls are not required to have safepoints within them (and many/most don’t), but they MAY include one or more safepoints. E.g. an unsafe memoryCopy MAY include a periodic safepoint opportunities (e.g. take a safepoint every 16KB). Zing sure does, as we do a lot of under the hood work to keep TTSP in check.

7. All [practical] JVMs apply some highly efficient mechanism for frequently crossing safepoint opportunities, where the thread does not actually enter a safepoint unless someone else indicates the need to do so. E.g. most call sites and loop backedges in generated code will include some sort of safepoint polling sequence that amounts to “do I need to go to a safepoint now?”. Many HotSpot variants (OpenJDK and Oracle JDK) currently use a simple global “go to safepoint” indicator in the form of a page that is protected when a safepoint is needed, and unprotected otherwise. The safepoint polling for this mechanism amounts to a load from a fixed address in that page. If the load traps with a SEGV, the thread knows it needs to go to enter a safepoint. Zing uses a different, per-thread go-to-safepoint indicator of similar efficiency.

8. All JNI code executes at a safepoint. No Java machine state of the executing thread can be changed or observed by it’s JNI code while at a safepoint. Any manipulation or observation of Java state done by JNI code is achieved via JNI API calls that leave the safepoint for the duration of the API call, and then enter a safepoint again before returning to the calling JNI code. This “crossing of the JNI line” is where most of the JNI call and API overhead lies, but it’s fairly quick (entering and leaving safepoint normally amounts to some CAS operations).

Examples of Operations that may encounter safepoints

  • Deoptimization
  • PrintThreads
  • PrintJNI
  • FindDeadlock
  • ThreadDump
  • EnableBiasLocking
  • RevokeBias
  • HeapDumper
  • GetAllStackTrace

References

http://psy-lob-saw.blogspot.com/2015/12/safepoints.html

http://chriskirk.blogspot.com/2013/09/what-is-java-safepoint.html

http://jpbempel.blogspot.com/2013/03/safety-first-safepoints.html

--

--

aarti gupta
software under the hood

-distributed computing enthusiast, staff engineer at VMware Inc