JNI和字節(jié)碼方法調(diào)用

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)方法俐巴,如下圖:

image.png

即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為例仁热,如下圖:
image.png

所有方法調(diào)用的API最終調(diào)用的都是jni.cpp中jni_invoke_static和jni_invoke_nonstatic方法,這兩個(gè)方法的調(diào)用如下圖:

image.png

其中jni_NewObject三個(gè)方法是通過(guò)jni_invoke_nonstatic調(diào)用類構(gòu)造方法

image.png

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)用有很大的不同

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末底循,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子衷恭,更是在濱河造成了極大的恐慌此叠,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件随珠,死亡現(xiàn)場(chǎng)離奇詭異灭袁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)窗看,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門茸歧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人显沈,你說(shuō)我怎么就攤上這事软瞎》昊剑” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵涤浇,是天一觀的道長(zhǎng)鳖藕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)只锭,這世上最難降的妖魔是什么著恩? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蜻展,結(jié)果婚禮上喉誊,老公的妹妹穿的比我還像新娘。我一直安慰自己纵顾,他們只是感情好伍茄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著施逾,像睡著了一般敷矫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上音念,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天沪饺,我揣著相機(jī)與錄音,去河邊找鬼闷愤。 笑死整葡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讥脐。 我是一名探鬼主播遭居,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼旬渠!你這毒婦竟也來(lái)了俱萍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤告丢,失蹤者是張志新(化名)和其女友劉穎枪蘑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岖免,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岳颇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了颅湘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片话侧。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖闯参,靈堂內(nèi)的尸體忽然破棺而出瞻鹏,到底是詐尸還是另有隱情悲立,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布新博,位于F島的核電站薪夕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叭披。R本人自食惡果不足惜寥殖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一玩讳、第九天 我趴在偏房一處隱蔽的房頂上張望涩蜘。 院中可真熱鬧,春花似錦熏纯、人聲如沸同诫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)误窖。三九已至,卻和暖如春秩贰,著一層夾襖步出監(jiān)牢的瞬間霹俺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工毒费, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丙唧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓觅玻,卻偏偏與公主長(zhǎng)得像想际,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子溪厘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容