Suppose I have this code (it really does not matter I think, but just in case here it is):
public class AtomicJDK9 {
static AtomicInteger ai = new AtomicInteger(0);
public static void main(String[] args) {
int sum = 0;
for (int i = 0; i < 30_000; ++i) {
sum += atomicIncrement();
}
System.out.println(sum);
}
public static int atomicIncrement() {
ai.getAndAdd(12);
return ai.get();
}
}
And here is how I am invoking it (using java-9):
java -XX:+UnlockDiagnosticVMOptions
-XX:-TieredCompilation
-XX:+PrintIntrinsics
AtomicJDK9
What I am trying to find out is what methods were replaced by intrinsic code. The first one that is hit (inside Unsafe):
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSwapIntVolatile(o, offset, v, v + delta));
return v;
}
And this method is indeed present in the output of the above invocation:
@ 8 jdk.internal.misc.Unsafe::getAndAddInt (27 bytes) (intrinsic)
But, the entire output is weird (for me that is):
@ 8 jdk.internal.misc.Unsafe::getAndAddInt (27 bytes) (intrinsic)
@ 3 jdk.internal.misc.Unsafe::getIntVolatile (0 bytes) (intrinsic)
@ 18 jdk.internal.misc.Unsafe::weakCompareAndSwapIntVolatile (11 bytes) (intrinsic)
@ 7 jdk.internal.misc.Unsafe::compareAndSwapInt (0 bytes) (intrinsic)
@ 8 jdk.internal.misc.Unsafe::getAndAddInt (27 bytes) (intrinsic)
Why is the getAndAddInt present twice in the output?
Also if getAndAddInt is indeed replaced by an intrinsic call, why is there a need to replace all other intrinsic methods down the call stackl they will not be used anymore. I do assume it's as simple as the stack of method calls is traversed from the bottom.
In order to illustrate the compiler logic I ran JVM with the following arguments.
-XX:-TieredCompilation -XX:CICompilerCount=1
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
And that's what it prints.
337 29 java.util.concurrent.atomic.AtomicInteger::getAndAdd (12 bytes)
@ 8 jdk.internal.misc.Unsafe::getAndAddInt (27 bytes) (intrinsic)
337 30 jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)
@ 3 jdk.internal.misc.Unsafe::getIntVolatile (0 bytes) (intrinsic)
@ 18 jdk.internal.misc.Unsafe::weakCompareAndSwapIntVolatile (11 bytes) (intrinsic)
338 32 jdk.internal.misc.Unsafe::weakCompareAndSwapIntVolatile (11 bytes)
@ 7 jdk.internal.misc.Unsafe::compareAndSwapInt (0 bytes) (intrinsic)
339 33 AtomicJDK9::atomicIncrement (16 bytes)
@ 5 java.util.concurrent.atomic.AtomicInteger::getAndAdd (12 bytes) inline (hot)
@ 8 jdk.internal.misc.Unsafe::getAndAddInt (27 bytes) (intrinsic)
@ 12 java.util.concurrent.atomic.AtomicInteger::get (5 bytes) accessor
AtomicInteger.getAndAdd
is called not only from your code, but from common JDK code, too.AtomicInteger.getAndAdd
reaches invocation threshold a little bit earlier than your AtomicJDK9.atomicIncrement
. Then getAndAdd
is submitted to compilation queue, and that's where the first intrinsic printout comes from.AtomicInteger.getAndAdd
is interpreted, Unsafe.getAndAddInt
and Unsafe.weakCompareAndSwapIntVolatile
methods also reach invocation threshold and begin compilation. The next 3 intrinsics are printed while compiling these Unsafe
methods.AtomicJDK9.atomicIncrement
also reaches invocation threashold and begin compilation. The last intrinsic printout corresponds to your method.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With