Java实例真的如此之快吗?

时间:2017-10-24 14:14:46

标签: java instanceof jmh

我试图测量instance of是否真的很快。这是一个非常简单的基准:

public Object a = 2;

@Benchmark
@Warmup(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@Measurement(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public boolean test() {
    return a instanceof Double;
}

我跑了这个板凳

Benchmark              Mode  Cnt  Score   Error  Units
MyBenchmark.test       avgt    5  3.105 ± 0.086  ns/op

基准测试的组件输出太长,省略。

我还写了一个简单的java程序

private static Object i = 123;

public static boolean insOf(){
    return i instanceof Double;
}

public static void main(String[] args) throws IOException {
    for (int i = 0; i < 100000000; i++)
        if(insOf())
            System.out.print("");
}

编译的insOf方法的程序集输出如下:

0x00007fd761114b60: mov     %eax,0xfffffffffffec000(%rsp)
  0x00007fd761114b67: push    %rbp
  0x00007fd761114b68: sub     $0x10,%rsp        ;*synchronization entry
                                                ; - com.get.intent.App::insOf@-1 (line 24)

  0x00007fd761114b6c: movabs  $0xd6f788e0,%r10  ;   {oop(a 'java/lang/Class' = 'com/get/intent/App')}
  0x00007fd761114b76: mov     0x68(%r10),%r11d  ;*getstatic i
                                                ; - com.get.intent.App::insOf@0 (line 24)

  0x00007fd761114b7a: mov     0x8(%r11),%r10d   ; implicit exception: dispatches to 0x00007fd761114b9c
  0x00007fd761114b7e: cmp     $0x20002192,%r10d  ;   {metadata('java/lang/Double')}
  0x00007fd761114b85: jne     0x7fd761114b98          <-------- HERE!!!
  0x00007fd761114b87: mov     $0x1,%eax
  0x00007fd761114b8c: add     $0x10,%rsp
  0x00007fd761114b90: pop     %rbp
  0x00007fd761114b91: test    %eax,0x16774469(%rip)  ;   {poll_return}
  0x00007fd761114b97: retq
  0x00007fd761114b98: xor     %eax,%eax
  0x00007fd761114b9a: jmp     0x7fd761114b8c          <------- HERE!!!
  0x00007fd761114b9c: mov     $0xfffffff4,%esi
  0x00007fd761114ba1: nop
  0x00007fd761114ba3: callq   0x7fd7610051a0    ; OopMap{off=72}
                                                ;*instanceof
                                                ; - com.get.intent.App::insOf@3 (line 24)
                                                ;   {runtime_call}
  0x00007fd761114ba8: callq   0x7fd776591a20    ;*instanceof
                                                ; - com.get.intent.App::insOf@3 (line 24)
                                                ;   {runtime_call}

省略了大量hlt条指令。

从我所看到的,实例是大约有十个具有两次跳转的汇编指令(jnejmp)。跳跃有点令人困惑。我们为什么需要它们?

问题: Java instance of真的这么快吗?

2 个答案:

答案 0 :(得分:2)

嗯,你正在进行反汇编,所以你可能需要重建那些跳转所代表的函数。使用源代码可用且有调试符号的JVM可能是个好主意。

基于instanceof的语义,我发出这些跳转所做的是为超类递归执行相同的测试(因为基本上instanceof可以用函数伪代码写成{ {1}}

答案 1 :(得分:1)

是的,对于大多数情况来说这是快速的,包括像你这样的琐碎案例。它首先乐观地检查类型的确切命中(在你的情况下为Double),然后回退到悲观地调用运行时的慢分支,因为可能需要遍历类层次结构。但是,在Double的情况下,编译器知道系统中不存在Double的子类,因此慢速分支通常是“假的”。

   mov     0x8(%r11),%r10d    ; read the klass ptr
   cmp     $0x20002192,%r10d  ;   klass for 'java/lang/Double'
   jne     NOT_EQUAL          ; not equal? slow branch
   mov     $0x1,%eax          ; return value = "true"
RETURN:
   add     $0x10,%rsp         ; epilog
   pop     %rbp
   test    %eax,0x16774469(%rip) 
   retq
NOT_EQUAL:
   xor     %eax,%eax          ; return value = "false"
   jmp     RETURN