Java對(duì)象創(chuàng)建的本質(zhì)就是按照對(duì)象的大小分配一塊內(nèi)存梆暮,然后完成屬性的初始化。對(duì)象創(chuàng)建完了府适,接著干啥了?調(diào)用Java方法完成特定功能肺樟。這就是我們接下來(lái)探討的主題檐春,Java方法調(diào)用是怎么實(shí)現(xiàn)的。
一么伯、Main方法
main方法是Java應(yīng)用啟動(dòng)執(zhí)行的入口方法疟暖,這個(gè)方法是怎么執(zhí)行的了?,關(guān)鍵代碼在OpenJDK jdk/src/share/bin/java.c中的int JNICALL JavaMain(void * _args)方法俐巴,如下圖:
即main方法是通過(guò)JNI的CallStaticVoidMethod方法執(zhí)行的疑枯。
二朴恳、JNI 方法調(diào)用
1饶深、API定義
JNI的方法調(diào)用的API奔浅,分為三種,總結(jié)如下:
- NativeType Call<type>Method:執(zhí)行非靜態(tài)方法調(diào)用邻遏,如果子類覆寫父類方法糠亩,則調(diào)用子類覆寫后的方法。
- NativeType CallNonvirtual<type>Method:Call<type>Method的擴(kuò)展版准验,如果子類覆寫父類方法赎线,可以根據(jù)jmethedId調(diào)用子類覆寫后的方法或者調(diào)用父類原來(lái)的方法,前者jmethodId從子類jclass中獲取糊饱,后者jmethodId從父類jclass中獲取垂寥。
- NativeType CallStatic<type>Method:執(zhí)行靜態(tài)方法調(diào)用。
三者根據(jù)傳遞參數(shù)的方式的不同另锋,又有三個(gè)“重載”方法滞项,以Call<type>Method為例:
- Call<type>Method:按照J(rèn)ava方法中定義的參數(shù)類型和順序依次傳遞參數(shù)即可,注意JNI中不支持基本類型的自動(dòng)裝箱拆箱夭坪,如果方法參數(shù)是包裝類文判,則需要調(diào)用包裝類的構(gòu)造方法構(gòu)造基本包裝類實(shí)例。
- Call<type>MethodA:按照J(rèn)ava方法中定義的參數(shù)類型和順序?qū)⑺袇?shù)放入一個(gè)jvalue數(shù)組中室梅,然后傳入該數(shù)組的指針即可戏仓,jvalue是一個(gè)union類型,可以支持所有的參數(shù)類型亡鼠。
Call<type>MethodV:按照J(rèn)ava方法中定義的參數(shù)類型和順序?qū)⑺袇?shù)放入一個(gè)va_list類中赏殃,該類表示一個(gè)參數(shù)列表。
通過(guò)宏的方式實(shí)現(xiàn)不同NativeType和type下的方法定義间涵,以Call<type>Method為例仁热,如下圖:
所有方法調(diào)用的API最終調(diào)用的都是jni.cpp中jni_invoke_static和jni_invoke_nonstatic方法,這兩個(gè)方法的調(diào)用如下圖:
其中jni_NewObject三個(gè)方法是通過(guò)jni_invoke_nonstatic調(diào)用類構(gòu)造方法
2浑厚、 jni_invoke_static
static void jni_invoke_static(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
//resolve_jmethod_id將jmethodID轉(zhuǎn)換成Method*
methodHandle method(THREAD, Method::resolve_jmethod_id(method_id));
// Create object to hold arguments for the JavaCall, and associate it with
// the jni parser
ResourceMark rm(THREAD);
//獲取方法參數(shù)個(gè)數(shù)
int number_of_parameters = method->size_of_parameters();
//初始化java_args
JavaCallArguments java_args(number_of_parameters);
args->set_java_argument_object(&java_args);
//校驗(yàn)?zāi)繕?biāo)方法為靜態(tài)方法
assert(method->is_static(), "method should be static");
//fingerprint返回目標(biāo)方法的fingerprint股耽,是一長(zhǎng)串?dāng)?shù)字,包含方法的參數(shù)類型钳幅,返回值類型等信息
/解析方法參數(shù)到JavaCallArguments中
args->iterate( Fingerprinter(method).fingerprint() );
// 設(shè)置返回結(jié)果的結(jié)果類型
result->set_type(args->get_ret_type());
//執(zhí)行方法調(diào)用
JavaCalls::call(result, method, &java_args, CHECK);
// 如果結(jié)果類型是對(duì)象或者數(shù)組類型,則需要將其轉(zhuǎn)換成本地引用
if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));
}
}
enum JNICallType {
JNI_STATIC, //靜態(tài)方法
JNI_VIRTUAL, //虛方法
JNI_NONVIRTUAL //非虛方法
};
inline static Method* resolve_jmethod_id(jmethodID mid) {
assert(mid != NULL, "JNI method id should not be null");
//據(jù)此可知炎滞,jmethodID實(shí)際是Method的指針的指針
return *((Method**)mid);
}
3敢艰、jni_invoke_nonstatic
static void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
//校驗(yàn)調(diào)用實(shí)例方法關(guān)聯(lián)的對(duì)象receiver不能為空
oop recv = JNIHandles::resolve(receiver);
if (recv == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
Handle h_recv(THREAD, recv);
int number_of_parameters;
Method* selected_method;
{
//將jmethodID轉(zhuǎn)換成Method*
Method* m = Method::resolve_jmethod_id(method_id);
//獲取方法個(gè)數(shù)
number_of_parameters = m->size_of_parameters();
//獲取此方法所屬的類Klass
Klass* holder = m->method_holder();
if (call_type != JNI_VIRTUAL) {
//如果是非虛方法調(diào)用,即CallNonvirtual<type>Method册赛,則使用指定的方法
//此時(shí)jmethodID從父類Klass獲取的則使用父類的實(shí)現(xiàn)钠导,如果使用子類Klass的實(shí)現(xiàn)則使用子類的實(shí)現(xiàn)
selected_method = m;
}
//虛方法調(diào)用震嫉,即Call<type>Method,itable即接口方法表
else if (!m->has_itable_index()) {
// non-interface call -- for that little speed boost, don't handlize
// 非接口方法調(diào)用
debug_only(No_Safepoint_Verifier nosafepoint;)
//校驗(yàn)該方法在虛方法表的索引是否有效牡属,如果目標(biāo)類已經(jīng)完成鏈接和初始化則valid_vtable_index()方法返回true
assert(m->valid_vtable_index(), "no valid vtable index");
//獲取虛方法表中的索引票堵,注意同一個(gè)方法,無(wú)論從子類Klass獲取還是從父類Klass獲取逮栅,其vtbl_index都是一樣的
int vtbl_index = m->vtable_index();
//如果vtbl_index不等于nonvirtual_vtable_index悴势,nonvirtual_vtable_index表示該方法不需要通過(guò)vtable分發(fā),即父類定義的final方法
if (vtbl_index != Method::nonvirtual_vtable_index) {
//獲取receiver對(duì)應(yīng)的Klass
Klass* k = h_recv->klass();
InstanceKlass *ik = (InstanceKlass*)k;
//獲取目標(biāo)Klass在指定虛方法表索引處的虛方法實(shí)現(xiàn)措伐,
//如receiver實(shí)際是子類實(shí)例特纤,jmethodID無(wú)論從父類Klass還是子類Klass獲取的,實(shí)際調(diào)用的都是子類的實(shí)現(xiàn)
selected_method = ik->method_at_vtable(vtbl_index);
} else {
//final方法
selected_method = m;
}
} else {
//接口方法
KlassHandle h_holder(THREAD, holder);
//獲取接口方法表中的索引,無(wú)論jmethodID從接口類Klass還是實(shí)現(xiàn)類Klass獲取的侥加,其itbl_index都是一樣的
int itbl_index = m->itable_index();
Klass* k = h_recv->klass();
//獲取接口方法捧存,使用receiver實(shí)例實(shí)際的類的接口實(shí)現(xiàn)
selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK);
}
}
methodHandle method(THREAD, selected_method);
/
ResourceMark rm(THREAD);
//初始化JavaCallArguments
JavaCallArguments java_args(number_of_parameters);
args->set_java_argument_object(&java_args);
//校驗(yàn)方法不是靜態(tài)方法
assert(!method->is_static(), "method should not be static");
//設(shè)置接受方法調(diào)用的對(duì)象實(shí)例
args->push_receiver(h_recv); // Push jobject handle
//解析方法參數(shù)
args->iterate( Fingerprinter(method).fingerprint() );
//設(shè)置方法返回類型
result->set_type(args->get_ret_type());
//調(diào)用方法
JavaCalls::call(result, method, &java_args, CHECK);
//處理結(jié)果返回值
if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));
}
}
從上述實(shí)現(xiàn)可知,jni_invoke_nonstatic比jni_invoke_static就是多了確認(rèn)實(shí)際調(diào)用方法的邏輯担败。CallNonvirtual<type>Method時(shí)和CallStatic<type>Method實(shí)際邏輯是一樣的昔穴,都是執(zhí)行直接執(zhí)行傳入的jmethodID對(duì)應(yīng)的Java方法,而Call<type>Method需要根據(jù)傳入的jmethodID確認(rèn)目標(biāo)方法的虛方法表索引或者接口方法表索引提前,然后根據(jù)索引獲取目標(biāo)實(shí)例對(duì)象所屬的類的實(shí)現(xiàn)方法傻咖。itable和vtable可參考《Hotspot Klass模型——Java類內(nèi)存表示機(jī)制》。
三岖研、方法調(diào)用字節(jié)碼指令
1卿操、指令定義
main方法通過(guò)JNI的方法調(diào)用開始執(zhí)行后,如果調(diào)用其他的Java方法就必須通過(guò)方法調(diào)用的字節(jié)碼指令孙援,JNI的方法調(diào)用僅限于本地方法實(shí)現(xiàn)使用害淤。執(zhí)行方法調(diào)用的字節(jié)碼指令總共有5個(gè),概述如下拓售,詳情可以參考《Java虛擬機(jī)規(guī)范》:
- invokedynamic:調(diào)用動(dòng)態(tài)方法窥摄,動(dòng)態(tài)方法是指最終被調(diào)用的方法可以在運(yùn)行期由程序動(dòng)態(tài)指定,從JDK7引入础淤,但是JDK7的編譯器無(wú)法直接使用該指令崭放,只能通過(guò)ASM字節(jié)碼編輯工具調(diào)用。目前主要用于JDK8中java.lang.invoke包和Lambda表達(dá)式鸽凶。
-invokeinteface:調(diào)用接口方法币砂,即由接口類中定義的方法,此時(shí)調(diào)用對(duì)象聲明的類型是接口類玻侥,實(shí)際的類型是該接口的某個(gè)實(shí)現(xiàn)類决摧。假如調(diào)用對(duì)象實(shí)例是C,查找具體執(zhí)行方法時(shí)先在C中查找名稱和描述符都和接口方法一致的方法,如果沒(méi)有找到則繼續(xù)在C的父類掌桩,父類的父類边锁,不斷往上遞歸查找,直到找到名稱和描述符都和接口方法一致的方法波岛,在編譯正常的情形下通過(guò)這兩步查找就可以確認(rèn)實(shí)際被執(zhí)行的方法茅坛。
-invokespecial:調(diào)用實(shí)例方法,包括父類方法则拷,私有方法和實(shí)例初始化方法三種贡蓖,實(shí)際被執(zhí)行的方法的查找邏輯同invokeinteface基本一樣,都是現(xiàn)在當(dāng)前類查找隔躲,再往上遞歸查找當(dāng)前類的父類摩梧,在編譯期即可確認(rèn)。
-invokestatic:調(diào)用靜態(tài)方法宣旱,在方法解析成功后仅父,如果方法所在的類或者接口沒(méi)有被初始化則指令執(zhí)行時(shí)會(huì)觸發(fā)其初始化。因?yàn)殪o態(tài)方法不能被繼承浑吟,因此只需在調(diào)用類中查找是否存在目標(biāo)方法笙纤,也是在編譯期即可確認(rèn)實(shí)際被執(zhí)行的方法。
-invokevirtual:調(diào)用實(shí)例方法组力,依據(jù)調(diào)用對(duì)象實(shí)例的類型進(jìn)行分派省容,如果目標(biāo)方法是簽名多態(tài)性方法(通常是java.lang.invoke.MethodHanlde的invoke和invokeExact方法),則需要做特殊處理燎字,以保證方法句柄能夠正常調(diào)用腥椒。注意invokevirtual并不是其字面描述的一樣的調(diào)用虛方法,調(diào)用某個(gè)子類獨(dú)有的方法(按C++的虛方法定義這種方法就是非虛方法)也是通過(guò)invokevirtual完成候衍,因?yàn)镴VM無(wú)法確認(rèn)方法調(diào)用實(shí)例是該子類的實(shí)例笼蛛,還是該子類的子類實(shí)例,后者可能覆寫了該方法蛉鹿。
測(cè)試用例如下:
package jni;
interface InterfaceA{
void say();
//接口中的default方法只是接口方法的默認(rèn)實(shí)現(xiàn)滨砍,接口實(shí)現(xiàn)類可以改寫默認(rèn)實(shí)現(xiàn)
default void print(){
System.out.println("InterfaceA default methoed");
}
static void staticDo(){
System.out.println("InterfaceA staticDo");
}
}
interface InterfaceB{
void interfaceDo();
}
class superB implements InterfaceA{
@Override
public void say() {
System.out.println("superB say");
}
@Override
public void print() {
System.out.println("superB print");
}
public void superDo(){
System.out.println("superB superDo ");
}
}
public class InvokeTest extends superB implements InterfaceB{
@Override
public void say() {
super.say();
System.out.println("InvokeTest say");
}
private void privateDo(){
System.out.println("InvokeTest privateDo");
}
public void subDo(){
privateDo();
System.out.println("InvokeTest subDo");
}
@Override
public void interfaceDo() {
System.out.println("InvokeTest interfaceDo");
}
public static void main(String[] args) {
InterfaceA.staticDo();
InvokeTest a=new InvokeTest();
a.say();
a.subDo();
a.print();
a.superDo();
a.interfaceDo();
superB b=a;
b.say();
b.print();
b.superDo();
InterfaceA c=a;
c.say();
c.print();
InterfaceB d=a;
d.interfaceDo();
}
}
編譯過(guò)后執(zhí)行javap -v可以查看具體的字節(jié)碼指令,截取部分如下:
public void say();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method jni/superB.say:()V 調(diào)用父類方法
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #4 // String InvokeTest say
9: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
public void subDo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #7 // Method privateDo:()V 調(diào)用私有方法
4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #8 // String InvokeTest subDo
9: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: invokestatic #10 // InterfaceMethod jni/InterfaceA.staticDo:()V 調(diào)用靜態(tài)方法
3: new #11 // class jni/InvokeTest
6: dup
7: invokespecial #12 // Method "<init>":()V 調(diào)用實(shí)例初始化方法
10: astore_1
11: aload_1
12: invokevirtual #13 // Method say:()V 調(diào)用實(shí)例的類型都是類妖异,所以這里都是invokevirtual
15: aload_1
16: invokevirtual #14 // Method subDo:()V subDo是子類特有的惋戏,為了兼容調(diào)用實(shí)例有可能是子類的子類的情形,所以依然使用invokevirtual指令
19: aload_1
20: invokevirtual #15 // Method print:()V
23: aload_1
24: invokevirtual #16 // Method superDo:()V
27: aload_1
28: invokevirtual #17 // Method interfaceDo:()V
31: aload_1
32: astore_2
33: aload_2
34: invokevirtual #2 // Method jni/superB.say:()V
37: aload_2
38: invokevirtual #18 // Method jni/superB.print:()V
41: aload_2
42: invokevirtual #19 // Method jni/superB.superDo:()V
45: aload_1
46: astore_3
47: aload_3
48: invokeinterface #20, 1 // InterfaceMethod jni/InterfaceA.say:()V 調(diào)用實(shí)例的類型都是接口類他膳,即使該接口方法已經(jīng)提供了默認(rèn)實(shí)現(xiàn)响逢,依然使用invokeinterface
53: aload_3
54: invokeinterface #21, 1 // InterfaceMethod jni/InterfaceA.print:()V
59: aload_1
60: astore 4
62: aload 4
64: invokeinterface #22, 1 // InterfaceMethod jni/InterfaceB.interfaceDo:()V
69: return
2、invokeinteface指令
該指令的實(shí)現(xiàn)參考OpenJDK8 hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp 2522行矩乐,源代碼說(shuō)明如下:
CASE(_invokeinterface): {
//pc表示當(dāng)前字節(jié)碼指令的地址龄句,get_native_u2返回從指定地址開始的2字節(jié)的數(shù)據(jù)回论,將其作為short讀取
//根據(jù)JVM規(guī)范散罕,index表示運(yùn)行時(shí)常量池的索引分歇,指向一個(gè)接口方法的符號(hào)引用
u2 index = Bytes::get_native_u2(pc+1);
//獲取常量池索引index處的值
ConstantPoolCacheEntry* cache = cp->entry_at(index);
if (!cache->is_resolved((Bytecodes::Code)opcode)) {
//如果接口方法未解析則解析
CALL_VM(InterpreterRuntime::resolve_invoke(THREAD, (Bytecodes::Code)opcode),
handle_exception);
cache = cp->entry_at(index);
}
istate->set_msg(call_method);
//特殊場(chǎng)景下java.lang.Object的虛方法調(diào)用,
if (cache->is_forced_virtual()) {
Method* callee;
//檢查操作數(shù)棧的方法參數(shù)是否為空
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
//如果是final方法
if (cache->is_vfinal()) {
//獲取解析后的方法
callee = cache->f2_as_vfinal_method();
//final方法調(diào)用
BI_PROFILE_UPDATE_FINALCALL();
} else {
//非final方法
// Get receiver.
int parms = cache->parameter_size();
//獲取執(zhí)行方法調(diào)用的對(duì)象實(shí)例
oop rcvr = STACK_OBJECT(-parms);
//校驗(yàn)rcvr是否為空
VERIFY_OOP(rcvr);
//獲取rcvr的實(shí)際類型Klass
InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();
//獲取虛方法表相同索引下的方法
callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
//profile統(tǒng)計(jì)
BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
}
//調(diào)用方法
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
istate->set_bcp_advance(5);
//方法調(diào)用完成返回
UPDATE_PC_AND_RETURN(0); // I'll be back...
}
// this could definitely be cleaned up QQQ
Method* callee;
//獲取接口方法
Method *interface_method = cache->f2_as_interface_method();
//獲取接口類
InstanceKlass* iclass = interface_method->method_holder();
// get receiver
int parms = cache->parameter_size();
//獲取方法調(diào)用對(duì)象實(shí)例
oop rcvr = STACK_OBJECT(-parms);
CHECK_NULL(rcvr);
//獲取方法調(diào)用對(duì)象實(shí)例的真實(shí)類型
InstanceKlass* int2 = (InstanceKlass*) rcvr->klass();
// Receiver subtype check against resolved interface klass (REFC).
{
//獲取目標(biāo)接口方法的resolved interface klass
Klass* refc = cache->f1_as_klass();
itableOffsetEntry* scan;
//遍歷int2實(shí)現(xiàn)的所有接口類欧漱,判斷是否存在目標(biāo)接口方法對(duì)應(yīng)的resolved interface klass
for (scan = (itableOffsetEntry*) int2->start_of_itable();
scan->interface_klass() != NULL;
scan++) {
if (scan->interface_klass() == refc) {
break;
}
}
// int2沒(méi)有實(shí)現(xiàn)目標(biāo)resolved interface klass职抡,拋出異常
if (scan->interface_klass() == NULL) {
VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
}
}
//遍歷int2實(shí)現(xiàn)的所有接口類,判斷是否存在目標(biāo)接口方法對(duì)應(yīng)InstanceKlass
itableOffsetEntry* ki = (itableOffsetEntry*) int2->start_of_itable();
int i;
for ( i = 0 ; i < int2->itable_length() ; i++, ki++ ) {
if (ki->interface_klass() == iclass) break;
}
// int2沒(méi)有實(shí)現(xiàn)目標(biāo)接口方法的InstanceKlass误甚,拋出異常
if (i == int2->itable_length()) {
VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
}
//獲取接口方法表的索引
int mindex = interface_method->itable_index();
//獲取rcvr的接口方法表第一個(gè)元素
itableMethodEntry* im = ki->first_method_entry(rcvr->klass());
//獲取接口方法表索引mindex處的方法
callee = im[mindex].method();
if (callee == NULL) {
VM_JAVA_ERROR(vmSymbols::java_lang_AbstractMethodError(), "", note_no_trap);
}
//profile統(tǒng)計(jì)
BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
//執(zhí)行方法調(diào)用
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
//字節(jié)碼指針往后移動(dòng)5個(gè),_invokeinterface的指令長(zhǎng)度是5
istate->set_bcp_advance(5);
UPDATE_PC_AND_RETURN(0); // I'll be back...
}
3缚甩、invokevirtual,invokespecial窑邦,invokestatic
invokevirtual擅威,invokespecial,invokestatic三個(gè)指令的實(shí)現(xiàn)都是一樣的冈钦,同樣也是參考bytecodeInterpreter.cpp 2634行郊丛,源代碼說(shuō)明如下:
CASE(_invokevirtual):
CASE(_invokespecial):
CASE(_invokestatic): {
//獲取運(yùn)行時(shí)常量池中目標(biāo)方法的符號(hào)引用的索引
u2 index = Bytes::get_native_u2(pc+1);
//獲取運(yùn)行時(shí)常量池指定索引的符號(hào)引用解析對(duì)象
ConstantPoolCacheEntry* cache = cp->entry_at(index);
if (!cache->is_resolved((Bytecodes::Code)opcode)) {
//如果未解析則解析符號(hào)引用
CALL_VM(InterpreterRuntime::resolve_invoke(THREAD, (Bytecodes::Code)opcode),
handle_exception);
cache = cp->entry_at(index);
}
istate->set_msg(call_method);
{
Method* callee;
//如果是invokevirtual指令
if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) {
//判斷方法調(diào)用實(shí)例對(duì)象是否為空
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
//如果是final方法
if (cache->is_vfinal()) {
//final方法不需走虛方法表分派,直接使用符號(hào)引用解析的結(jié)果
callee = cache->f2_as_vfinal_method();
// Profile final方法調(diào)用統(tǒng)計(jì)
BI_PROFILE_UPDATE_FINALCALL();
} else {
// get receiver
int parms = cache->parameter_size();
//獲取方法調(diào)用的實(shí)例對(duì)象
oop rcvr = STACK_OBJECT(-parms);
VERIFY_OOP(rcvr);
//獲取方法調(diào)用實(shí)例對(duì)象的實(shí)際klass
InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();
//獲取虛方法表中相同索引處的方法
callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
//vitual 調(diào)用 profile統(tǒng)計(jì)
BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
}
} else {
//invokespecial指令
if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) {
//判斷方法調(diào)用實(shí)例對(duì)象是否為空
CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
}
//invokespecial指令調(diào)用的方法不需要分派瞧筛,直接使用符號(hào)引用解析的方法
callee = cache->f1_as_method();
// Profile統(tǒng)計(jì)
BI_PROFILE_UPDATE_CALL();
}
//執(zhí)行方法調(diào)用
istate->set_callee(callee);
istate->set_callee_entry_point(callee->from_interpreted_entry());
//字節(jié)碼指針往后移動(dòng)3個(gè)厉熟,invokevirtual,invokespecial较幌,invokestatic三個(gè)指令的指令長(zhǎng)度都是3
istate->set_bcp_advance(3);
UPDATE_PC_AND_RETURN(0); // I'll be back...
}
}
從上述源碼分析可知揍瑟,invokevirtual,invokespecial乍炉,invokestatic绢片,invokeinteface四個(gè)指令在找到了正確的執(zhí)行方法后,就直接通過(guò)JVM解釋器跳轉(zhuǎn)到對(duì)應(yīng)方法的執(zhí)行了岛琼,跟JNI中方法調(diào)用有很大的不同