都说调用接口要比调用继承类要慢,但慢在何处?
先看byteCodeInterpreter.cpp里面对这invokevirtual和invokeInterface的区别。
CASE(_invokeinterface): { //调用接口
u2 index = Bytes::get_native_u2(pc+1);
ConstantPoolCacheEntry* cache = cp->entry_at(index);
methodOop callee;
klassOop iclass = (klassOop)cache->f1();
int parms = cache->parameter_size();
oop rcvr = STACK_OBJECT(-parms);
CHECK_NULL(rcvr);
instanceKlass* int2 = (instanceKlass*) rcvr->klass()->klass_part();
itableOffsetEntry* ki = (itableOffsetEntry*) int2->start_of_itable();
int i;
for ( i = 0 ; i < int2->itable_length() ; i++, ki++ ) {//搜索整个接口表,进行比较,直至找到
if (ki->interface_klass() == iclass) break;
}
.......
int mindex = cache->f2();
itableMethodEntry* im = ki->first_method_entry(rcvr->klass());
callee = im[mindex].method();//通过找到的接口,找到要调用的方法
而invokevirtual(调用继承类)
CASE(_invokevirtual):
u2 index = Bytes::get_native_u2(pc+1);
ConstantPoolCacheEntry* cache = cp->entry_at(index);
methodOop callee;
int parms = cache->parameter_size();
instanceKlass* rcvrKlass = (instanceKlass*) STACK_OBJECT(-parms)->klass()->klass_part();
callee = (methodOop) rcvrKlass->start_of_vtable()[ cache->f2()]; //直接调用方法
由上面可见,最大的区别就是接口调用每次都需要搜索接口表,而调用继承类可以直接找到。
再看看权威书籍《深入java虚拟机》P336页给出的答案,“java虚拟机使用不同于类引用的操作码来调用接口引用的方法,这是因为java不能象使用引用那样,使用许多与方法表偏移量相关的假设。对于类引用来说,无论对象实际的类是什么,方法在方法表始终占据相同的位置。但对于接口引用来说,情况就不是这样了,位于不同类的同一个方法所占据的位置是不同的,尽管这些类实现同一个接口。”
由此可知,调用接口引用方法可能要比调用类引用要慢。一般情况下,单个类实现的接口都不多,其实这方面的效率影响还是蛮小的