Java9中内在行为不清晰

8

假设我有以下代码(我认为这并不重要,但以防万一,在这里):

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();
     }
}

以下是我如何调用它(使用java-9):

 java -XX:+UnlockDiagnosticVMOptions 
      -XX:-TieredCompilation  
      -XX:+PrintIntrinsics  
      AtomicJDK9

我想了解的是哪些方法被内在代码所替代。第一个被调用的方法位于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;
      }

而且这种方法确实存在于上述调用的输出中:

 @ 8   jdk.internal.misc.Unsafe::getAndAddInt (27 bytes)   (intrinsic)

但是,整个输出对我来说很奇怪:
 @ 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)

为什么输出中会出现两次getAndAddInt
如果确实用内置调用替换了getAndAddInt,为什么需要替换调用栈中的所有其他内置方法呢?它们将不再被使用。我假设这就像从底部遍历方法调用堆栈一样简单。

1
也许可以添加+PrintCompilation(或者任何新的统一日志等效项),并阐明上下文。 - the8472
1个回答

8
为了说明编译器逻辑,我使用以下参数运行了JVM。
    -XX:-TieredCompilation -XX:CICompilerCount=1
    -XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining 

这就是它打印的内容。

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 不仅从您的代码中调用,而且还从常见的 JDK 代码中调用。
  • 也就是说,AtomicInteger.getAndAdd 比您的 AtomicJDK9.atomicIncrement 提前达到调用阈值。然后,getAndAdd 被提交到编译队列中,这就是第一个内置打印输出的地方。
  • HotSpot JVM 在后台编译方法。当方法正在被编译时,执行将在解释器中继续。
  • 当解释 AtomicInteger.getAndAdd 时,Unsafe.getAndAddIntUnsafe.weakCompareAndSwapIntVolatile 方法也达到了调用阈值并开始编译。下面的三个内置打印输出是在编译这些 Unsafe 方法时生成的。
  • 最后,AtomicJDK9.atomicIncrement 也达到了调用阈值并开始编译。最后一个内置打印输出对应于您的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接