與invokevirtual指令類似,當沒有對目標方法進行解析時,需要調用LinkResolver::resolve_invoke()函數進行解析,這個函數會調用其它一些函數完成方法的解析,如下圖所示。

上圖中粉色的部分與解析invokevirtual字節碼指令有所區別,resolve_pool()函數及其調用的相關函數在介紹invokevirtual字節碼指令時詳細介紹過,這里不再介紹。
調用LinkResolver::resolve_invokeinterface()函數對字節碼指令進行解析。函數的實現如下:
void LinkResolver::resolve_invokeinterface(
CallInfo& result,
Handle recv,
constantPoolHandle pool,
int index, // 指的是常量池緩存項的索引
TRAPS
) {
KlassHandle resolved_klass;
Symbol* method_name = NULL;
Symbol* method_signature = NULL;
KlassHandle current_klass;
// 解析常量池時,傳入的參數pool(根據當前棧中要執行的方法找到對應的常量池)和
// index(常量池緩存項的緩存,還需要映射為原常量池索引)是有值的,根據這兩個值能夠
// 解析出resolved_klass和要查找的方法名稱method_name和方法簽名method_signature
resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK);
KlassHandle recvrKlass (THREAD, recv.is_null() ? (Klass*)NULL : recv->klass());
resolve_interface_call(result, recv, recvrKlass, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK);
}
我們接着看resolve_interface_call()函數的實現,如下:
void LinkResolver::resolve_interface_call(
CallInfo& result,
Handle recv,
KlassHandle recv_klass,
KlassHandle resolved_klass,
Symbol* method_name,
Symbol* method_signature,
KlassHandle current_klass,
bool check_access,
bool check_null_and_abstract,
TRAPS
) {
methodHandle resolved_method;
linktime_resolve_interface_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK);
runtime_resolve_interface_method(result, resolved_method, resolved_klass, recv, recv_klass, check_null_and_abstract, CHECK);
}
調用2個函數對方法進行解析。首先看linktime_resolve_interface_method()函數的實現。
調用linktime_resolve_interface_method()函數會調用LinkResolver::resolve_interface_method()函數,此函數的實現如下:
void LinkResolver::resolve_interface_method(
methodHandle& resolved_method,
KlassHandle resolved_klass,
Symbol* method_name,
Symbol* method_signature,
KlassHandle current_klass,
bool check_access,
bool nostatics,
TRAPS
) {
// 從接口和父類java.lang.Object中查找方法,包括靜態方法
lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, false, true, CHECK);
if (resolved_method.is_null()) {
// 從實現的所有接口中查找方法
lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK);
if (resolved_method.is_null()) {
// no method found
// ...
}
}
// ...
}
首先調用LinkResolver::lookup_method_in_klasses()函數進行方法查找,在之前介紹過invokevirtual字節碼指令時介紹過這個函數,不過只介紹了與invokevirtual指令相關的處理邏輯,這里需要繼續查看invokeinterface的相關處理邏輯,實現如下:
void LinkResolver::lookup_method_in_klasses(
methodHandle& result,
KlassHandle klass,
Symbol* name,
Symbol* signature,
bool checkpolymorphism,
// 對於invokevirtual來說,值為false,對於invokeinterface來說,值為true
bool in_imethod_resolve,
TRAPS
) {
Method* result_oop = klass->uncached_lookup_method(name, signature);
// 在接口中定義方法的解析過程中,忽略Object類中的靜態和非public方法,如
// clone、finalize、registerNatives
if (
in_imethod_resolve &&
result_oop != NULL &&
klass->is_interface() &&
(result_oop->is_static() || !result_oop->is_public()) &&
result_oop->method_holder() == SystemDictionary::Object_klass() // 方法定義在Object類中
) {
result_oop = NULL;
}
if (result_oop == NULL) {
Array<Method*>* default_methods = InstanceKlass::cast(klass())->default_methods();
if (default_methods != NULL) {
result_oop = InstanceKlass::find_method(default_methods, name, signature);
}
}
// ...
result = methodHandle(THREAD, result_oop);
}
調用uncached_lookup_method()函數從當前類和父類中查找,如果沒有找到或找到的是Object類中的不合法方法,則會調用find_method()函數從默認方法中查找。在Java8的新特性中有一個新特性為接口默認方法,該新特性允許我們在接口中添加一個非抽象的方法實現,而這樣做的方法只需要使用關鍵字default修飾該默認實現方法即可。
uncached_lookup_method()函數的實現如下:
Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
Klass* klass = const_cast<InstanceKlass*>(this);
bool dont_ignore_overpasses = true;
while (klass != NULL) {
Method* method = InstanceKlass::cast(klass)->find_method(name, signature);
if ((method != NULL) && (dont_ignore_overpasses || !method->is_overpass())) {
return method;
}
klass = InstanceKlass::cast(klass)->super();
dont_ignore_overpasses = false; // 不要搜索父類中的overpass方法
}
return NULL;
}
從當前類和父類中查找方法。當從類和父類中查找方法時,調用find_method()函數,最終調用另外一個重載函數find_method()從InstanceKlass::_methods屬性中保存的方法中進行查找;當從默認方法中查找方法時,調用find_method()函數從InstanceKlass::_default_methods屬性中保存的方法中查找。重載的find_method()函數的實現如下:
Method* InstanceKlass::find_method(Array<Method*>* methods, Symbol* name, Symbol* signature) {
int hit = find_method_index(methods, name, signature);
return hit >= 0 ? methods->at(hit): NULL;
}
其實調用find_method_index()函數就是根據二分查找來找名稱為name,簽名為signature的方法,因為InstanceKlass::_methods和InstanceKlass::_default_methods屬性中的方法已經進行了排序,關於這些函數中存儲的方法及如何進行排序在《深入剖析Java虛擬機:源碼剖析與實例詳解(基礎卷)》一書中詳細介紹過,這里不再介紹。
調用的LinkResolver::runtime_resolve_interface_method()函數的實現如下:
void LinkResolver::runtime_resolve_interface_method(
CallInfo& result,
methodHandle resolved_method,
KlassHandle resolved_klass,
Handle recv,
KlassHandle recv_klass,
bool check_null_and_abstract, // 對於invokeinterface來說,值為false
TRAPS
) {
// ...
methodHandle sel_method;
lookup_instance_method_in_klasses(
sel_method,
recv_klass,
resolved_method->name(),
resolved_method->signature(),
CHECK);
if (sel_method.is_null() && !check_null_and_abstract) {
sel_method = resolved_method;
}
// ...
// 如果查找接口的實現時找到的是Object類中的方法,那么要通過vtable進行分派,所以我們需要
// 更新的是vtable相關的信息
if (!resolved_method->has_itable_index()) {
int vtable_index = resolved_method->vtable_index();
assert(vtable_index == sel_method->vtable_index(), "sanity check");
result.set_virtual(resolved_klass, recv_klass, resolved_method, sel_method, vtable_index, CHECK);
} else {
int itable_index = resolved_method()->itable_index();
result.set_interface(resolved_klass, recv_klass, resolved_method, sel_method, itable_index, CHECK);
}
}
當沒有itable索引時,通過vtable進行動態分派;否則通過itable進行動態分派。
調用的lookup_instance_method_in_klasses()函數的實現如下:
void LinkResolver::lookup_instance_method_in_klasses(
methodHandle& result,
KlassHandle klass,
Symbol* name,
Symbol* signature,
TRAPS
) {
Method* result_oop = klass->uncached_lookup_method(name, signature);
result = methodHandle(THREAD, result_oop);
// 循環查找方法的實現,不會查找靜態方法
while (!result.is_null() && result->is_static() && result->method_holder()->super() != NULL) {
KlassHandle super_klass = KlassHandle(THREAD, result->method_holder()->super());
result = methodHandle(THREAD, super_klass->uncached_lookup_method(name, signature));
}
// 當從擁有Itable的類或父類中找到接口中方法的實現時,result不為NULL,
// 否則為NULL,這時候就要查找默認的方法實現了,這也算是一種實現
if (result.is_null()) {
Array<Method*>* default_methods = InstanceKlass::cast(klass())->default_methods();
if (default_methods != NULL) {
result = methodHandle(InstanceKlass::find_method(default_methods, name, signature));
}
}
}
如上在查找默認方法實現時會調用find_method()函數,此函數在之前介紹invokevirtual字節碼指令的解析過程時詳細介紹過,這里不再介紹。
在LinkResolver::runtime_resolve_interface_method()函數的最后有可能調用CallInfo::set_interface()或CallInfo::set_virtual()函數,調用這兩個函數就是將查找到的信息保存到CallInfo實例中。最終會在InterpreterRuntime::resolve_invoke()函數中根據CallInfo實例中保存的信息更新ConstantPoolCacheEntry相關的信息,如下:
switch (info.call_kind()) {
// ...
case CallInfo::itable_call:
cache_entry(thread)->set_itable_call(
bytecode,
info.resolved_method(),
info.itable_index());
break;
default: ShouldNotReachHere();
}
當CallInfo中保存的是itable的分派信息時,調用set_itable_call()函數,這個函數的實現如下:
void ConstantPoolCacheEntry::set_itable_call(
Bytecodes::Code invoke_code,
methodHandle method,
int index
) {
assert(invoke_code == Bytecodes::_invokeinterface, "");
InstanceKlass* interf = method->method_holder();
// interf一定是接口,而method一定是非final方法
set_f1(interf); // 對於itable,_f1保存的是表示接口的InstanceKlass
set_f2(index); // 對於itable,_f2保存的是itable索引
set_method_flags(as_TosState(method->result_type()),
0, // no option bits
method()->size_of_parameters());
set_bytecode_1(Bytecodes::_invokeinterface);
}
使用CallInfo實例中的信息更新ConstantPoolCacheEntry中的信息即可。
推薦閱讀:
第2篇-JVM虛擬機這樣來調用Java主類的main()方法
第13篇-通過InterpreterCodelet存儲機器指令片段
第20篇-加載與存儲指令之ldc與_fast_aldc指令(2)
第21篇-加載與存儲指令之iload、_fast_iload等(3)

