Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a lightweight method which adds a safepoint in Java 9+

Tags:

java

jvm

java-11

Is there a cheaper method call in Java 9+ which keeps its safepoint?

The JVM removes safepoints at runtime to improve efficiency however this can make profiling and monitoring the code more difficult. For this reason, we deliberately add trivial calls in carefully selected places to ensure there is a safepoint present.

public static void safepoint() {
    if (IS_JAVA_9_PLUS)
        Thread.holdsLock(""); // 100 ns on Java 11
    else
        Compiler.enable(); // 5 ns on Java 8
}

public static void optionalSafepoint() {
    if (SAFEPOINT_ENABLED)
        if (IS_JAVA_9_PLUS)
            Thread.holdsLock("");
        else
            Compiler.enable();
}

On Java 8 this overhead is fine, but Compiler.enable() gets optimised away in Java 9+ so we have to use a much more expensive method, or not enable this feature.

EDIT: Apart from profilers, I have used the safepoint() to get better detail from Thread.getStackTrace() so the application can profile itself e.g. when it takes too long to perform an action.

like image 605
Peter Lawrey Avatar asked Jun 24 '20 08:06

Peter Lawrey


People also ask

What is safepoint in Java?

All JVM's use safepoints to bring all of the application threads to a known state so the JVM can perform certain operations. Safepoints are used during Garbage Collection, during JIT compilation, for Thread Dumps, and many other operations.

What is a safepoint?

A safepoint is a point in program execution where the state of the program is known and can be examined. Things like registers, memory, etc. For the JVM to completely pause and run tasks (such as GC), all threads must come to a safepoint. For example, to retrieve a stack trace on a thread we must come to a safepoint.


1 Answers

In HotSpot JVM, safepoints (where the JVM can safely stop Java threads) are

  • before the return from a non-inlined method;
  • at backward branches (i.e. in loops), unless the loop is counted. The loop is counted, if it is known to have finite number of iterations, and the loop variable fits integer type;
  • at thread state transitions (native -> Java, native -> VM);
  • in the blocking functions of the JVM runtime.

All the above places, except backward branches, imply at least a method call overhead. So, apparently the cheapest way to put a safepoint is to write a non-counted loop:

public class Safepoint {
    private static volatile int one = 1;

    public static void force() {
        for (int i = 0; i < one; i++) ;
    }
}

volatile guarantees that the loop will not be eliminated by the optimizer, and it will not be treated as counted.

I verified with -XX:+PrintAssembly that the safepoint poll instruction is inserted wherever I call Safepoint.force(). The call itself takes about 1 ns.

However, due to a bug in JDK 8, the existence of safepoint polls does not yet guarantee the correctness of stack traces obtained from a different thread. A native method call sets the last Java frame anchor, and thus "repairs" the stack trace. I guess this was a reason why you chose a native method. The bug was fixed in JDK 9+ though.

BTW, here is a couple of native methods that have lower overhead than Thread.holdsLock:

Thread.currentThread().isAlive()
Runtime.getRuntime().totalMemory()

As to profiling, the safepoint-based profilers are completely broken in the first place. This is actually a reason why I started async-profiler project a few years ago. Its goal is to facilitate profiling of Java applications with low overhead and with no safepoint bias.

like image 88
apangin Avatar answered Oct 21 '22 04:10

apangin