Java默认方法比相同的代码慢,但在抽象类中

我有一个PackedObject接口:

 public interface PackedObject { int get(); int sum(); void setIndex(int index); default int defaultSum() { return get(); } } 

抽象类AbstractPackedObject

 public abstract class AbstractPackedObject implements PackedObject { protected int index = 0; protected int[] buffer; public void setIndex(int index) { this.index = index; } public void setBuffer(int[] buffer) { this.buffer = buffer; } @Override public int sum(){ return get(); } } 

并具体实现WrappedPackedObject

 public class WrappedPackedObject extends AbstractPackedObject implements PackedObject { public WrappedPackedObject(int[] buffer) { this.buffer = buffer; } @Override public int get() { return buffer[index]; } } 

我对defaultSumsum方法进行了基准测试(JMH基准测试的片段):

  for (int i = 0; i < NB; i++) { packedObject.setIndex(i); value += packedObject.defaultSum(); } for (int i = 0; i < NB; i++) { packedObject.setIndex(i); value += packedObject.sum(); } 

我试图找出为什么sum基准测试器比defaultSum基准测试快1.7倍。

我已经开始深入研究JIT奥术了。 调用网站目标只有一种方法,所以我期待内联完成。 打印内联的输出如下:

 @ 25 com.github.nithril.PackedObject::defaultSum (7 bytes) inline (hot) \-> TypeProfile (479222/479222 counts) = com/github/nithril/WrappedPackedObject @ 1 com.github.nithril.WrappedPackedObject::get (14 bytes) inline (hot) @ 10 java.nio.DirectByteBuffer::getInt (15 bytes) inline (hot) @ 25 com.github.nithril.AbstractPackedObject::sum (5 bytes) inline (hot) @ 1 com.github.nithril.WrappedPackedObject::get (14 bytes) inline (hot) @ 10 java.nio.DirectByteBuffer::getInt (15 bytes) inline (hot) 

我还不明白为什么这行出现TypeProfile (479222/479222 counts) = com/github/nithril/WrappedPackedObject

我用上面的代码创建了一个专门的项目 。 基准测试使用JMH完成。

谢谢你的帮助。

编辑2015/05/20:

我简化了java代码。

benchSum的内部循环非常简单:

 0x00007f1bb11afb84: add 0x10(%r10,%r8,4),%eax ;*iadd ; - com.github.nithril.PackedObjectBench::benchSum@29 (line 50) 0x00007f1bb11afb89: mov %r8d,0xc(%r12,%r11,8) ;*putfield index ; - com.github.nithril.AbstractPackedObject::setIndex@2 (line 13) ; - com.github.nithril.PackedObjectBench::benchSum@17 (line 49) 0x00007f1bb11afb8e: inc %r8d ;*iinc ; - com.github.nithril.PackedObjectBench::benchSum@31 (line 48) 0x00007f1bb11afb91: cmp $0x2710,%r8d 0x00007f1bb11afb98: jl 0x00007f1bb11afb84 

benchDefaultSum的内部循环更复杂,索引的读/写和内部循环内部数组绑定的比较。 我还没有完全理解这种比较的目的……

 0x00007fcfdcf82cb8: mov %edx,0xc(%r12,%r11,8) ;*putfield index ; - com.github.nithril.AbstractPackedObject::setIndex@2 (line 13) ; - com.github.nithril.PackedObjectBench::benchDefaultSum@17 (line 32) 0x00007fcfdcf82cbd: mov 0xc(%r10),%r8d ;*getfield index ; - com.github.nithril.WrappedPackedObject::get@5 (line 17) ; - com.github.nithril.PackedObject::defaultSum@1 (line 15) ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33) 0x00007fcfdcf82cc1: cmp %r9d,%r8d 0x00007fcfdcf82cc4: jae 0x00007fcfdcf82d1f ;*iaload ; - com.github.nithril.WrappedPackedObject::get@8 (line 17) ; - com.github.nithril.PackedObject::defaultSum@1 (line 15) ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33) 0x00007fcfdcf82cc6: add 0x10(%rcx,%r8,4),%eax ;*iadd ; - com.github.nithril.PackedObjectBench::benchDefaultSum@29 (line 33) 0x00007fcfdcf82ccb: inc %edx ;*iinc ; - com.github.nithril.PackedObjectBench::benchDefaultSum@31 (line 31) 0x00007fcfdcf82ccd: cmp $0x2710,%edx 0x00007fcfdcf82cd3: jl 0x00007fcfdcf82cb8 ;*aload_2 [...] 0x00007fcfdcf82ce6: mov $0xffffffe4,%esi 0x00007fcfdcf82ceb: mov %r10,0x8(%rsp) 0x00007fcfdcf82cf0: mov %ebx,0x4(%rsp) 0x00007fcfdcf82cf4: mov %r8d,0x10(%rsp) 0x00007fcfdcf82cf9: xchg %ax,%ax 0x00007fcfdcf82cfb: callq 0x00007fcfdcdea1a0 ; OopMap{rbp=NarrowOop [8]=Oop off=416} ;*iaload ; - com.github.nithril.WrappedPackedObject::get@8 (line 17) ; - com.github.nithril.PackedObject::defaultSum@1 (line 15) ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33) ; {runtime_call} 0x00007fcfdcf82d00: callq 0x00007fcff1c94320 ;*iaload ; - com.github.nithril.WrappedPackedObject::get@8 (line 17) ; - com.github.nithril.PackedObject::defaultSum@1 (line 15) ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33) ; {runtime_call} [...] 0x00007fcfdcf82d1f: mov %eax,(%rsp) 0x00007fcfdcf82d22: mov %edx,%ebx 0x00007fcfdcf82d24: jmp 0x00007fcfdcf82ce6 

只是粗略地反复通过粗略阅读hotspot-compiler-dev邮件列表而获取的信息,但这可能是缺少接口中默认方法的类层次结构分析 ,这会阻止接口方法的虚拟化。

请参阅JDK Bug 8065760和6986483


我的猜测是,即使该方法是内联的,它仍然是一个类型保护,在抽象情况下被CHA消除,但不是接口方法。

打印优化组件(我认为JMH有一些标志)可以证实这一点。