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

Java對象創(chuàng)建的本質(zhì)就是按照對象的大小分配一塊內(nèi)存,然后完成屬性的初始化。對象創(chuàng)建完了,接著干啥了?調(diào)用Java方法完成特定功能。這就是我們接下來探討的主題,Java方法調(diào)用是怎么實現(xiàn)的。

一、Main方法
main方法是Java應(yīng)用啟動執(zhí)行的入口方法,這個方法是怎么執(zhí)行的了?,關(guān)鍵代碼在OpenJDK jdk/src/share/bin/java.c中的int JNICALL JavaMain(void * _args)方法,如下圖:

image.png

即main方法是通過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的擴展版,如果子類覆寫父類方法,可以根據(jù)jmethedId調(diào)用子類覆寫后的方法或者調(diào)用父類原來的方法,前者jmethodId從子類jclass中獲取,后者jmethodId從父類jclass中獲取。
  • NativeType CallStatic<type>Method:執(zhí)行靜態(tài)方法調(diào)用。

三者根據(jù)傳遞參數(shù)的方式的不同,又有三個“重載”方法,以Call<type>Method為例:

  • Call<type>Method:按照Java方法中定義的參數(shù)類型和順序依次傳遞參數(shù)即可,注意JNI中不支持基本類型的自動裝箱拆箱,如果方法參數(shù)是包裝類,則需要調(diào)用包裝類的構(gòu)造方法構(gòu)造基本包裝類實例。
  • Call<type>MethodA:按照Java方法中定義的參數(shù)類型和順序?qū)⑺袇?shù)放入一個jvalue數(shù)組中,然后傳入該數(shù)組的指針即可,jvalue是一個union類型,可以支持所有的參數(shù)類型。
    Call<type>MethodV:按照Java方法中定義的參數(shù)類型和順序?qū)⑺袇?shù)放入一個va_list類中,該類表示一個參數(shù)列表。
    通過宏的方式實現(xiàn)不同NativeType和type下的方法定義,以Call<type>Method為例,如下圖:
image.png

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

image.png

其中jni_NewObject三個方法是通過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ù)個數(shù)
  int number_of_parameters = method->size_of_parameters();
  //初始化java_args
  JavaCallArguments java_args(number_of_parameters);
  args->set_java_argument_object(&java_args);
  //校驗?zāi)繕朔椒殪o態(tài)方法
  assert(method->is_static(), "method should be static");
 
  //fingerprint返回目標方法的fingerprint,是一長串數(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é)果類型是對象或者數(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實際是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) {
  //校驗調(diào)用實例方法關(guān)聯(lián)的對象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);
    //獲取方法個數(shù)
    number_of_parameters = m->size_of_parameters();
    //獲取此方法所屬的類Klass
    Klass* holder = m->method_holder();
    if (call_type != JNI_VIRTUAL) {
        //如果是非虛方法調(diào)用,即CallNonvirtual<type>Method,則使用指定的方法
        //此時jmethodID從父類Klass獲取的則使用父類的實現(xiàn),如果使用子類Klass的實現(xiàn)則使用子類的實現(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;)
      //校驗該方法在虛方法表的索引是否有效,如果目標類已經(jīng)完成鏈接和初始化則valid_vtable_index()方法返回true
      assert(m->valid_vtable_index(), "no valid vtable index");
      //獲取虛方法表中的索引,注意同一個方法,無論從子類Klass獲取還是從父類Klass獲取,其vtbl_index都是一樣的
      int vtbl_index = m->vtable_index();
 
      //如果vtbl_index不等于nonvirtual_vtable_index,nonvirtual_vtable_index表示該方法不需要通過vtable分發(fā),即父類定義的final方法
      if (vtbl_index != Method::nonvirtual_vtable_index) {
        //獲取receiver對應(yīng)的Klass
        Klass* k = h_recv->klass();
        InstanceKlass *ik = (InstanceKlass*)k;
        //獲取目標Klass在指定虛方法表索引處的虛方法實現(xiàn),
        //如receiver實際是子類實例,jmethodID無論從父類Klass還是子類Klass獲取的,實際調(diào)用的都是子類的實現(xiàn)
        selected_method = ik->method_at_vtable(vtbl_index);
      } else {
        //final方法
        selected_method = m;
      }
    } else {
      //接口方法
      KlassHandle h_holder(THREAD, holder);
      //獲取接口方法表中的索引,無論jmethodID從接口類Klass還是實現(xiàn)類Klass獲取的,其itbl_index都是一樣的
      int itbl_index = m->itable_index();
      Klass* k = h_recv->klass();
      //獲取接口方法,使用receiver實例實際的類的接口實現(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);
 
  //校驗方法不是靜態(tài)方法
  assert(!method->is_static(), "method should not be static");
  //設(shè)置接受方法調(diào)用的對象實例
  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()));
  }
}

從上述實現(xiàn)可知,jni_invoke_nonstatic比jni_invoke_static就是多了確認實際調(diào)用方法的邏輯。CallNonvirtual<type>Method時和CallStatic<type>Method實際邏輯是一樣的,都是執(zhí)行直接執(zhí)行傳入的jmethodID對應(yīng)的Java方法,而Call<type>Method需要根據(jù)傳入的jmethodID確認目標方法的虛方法表索引或者接口方法表索引,然后根據(jù)索引獲取目標實例對象所屬的類的實現(xiàn)方法。itable和vtable可參考《Hotspot Klass模型——Java類內(nèi)存表示機制》。

三、方法調(diào)用字節(jié)碼指令
1、指令定義
main方法通過JNI的方法調(diào)用開始執(zhí)行后,如果調(diào)用其他的Java方法就必須通過方法調(diào)用的字節(jié)碼指令,JNI的方法調(diào)用僅限于本地方法實現(xiàn)使用。執(zhí)行方法調(diào)用的字節(jié)碼指令總共有5個,概述如下,詳情可以參考《Java虛擬機規(guī)范》:

  • invokedynamic:調(diào)用動態(tài)方法,動態(tài)方法是指最終被調(diào)用的方法可以在運行期由程序動態(tài)指定,從JDK7引入,但是JDK7的編譯器無法直接使用該指令,只能通過ASM字節(jié)碼編輯工具調(diào)用。目前主要用于JDK8中java.lang.invoke包和Lambda表達式。
    -invokeinteface:調(diào)用接口方法,即由接口類中定義的方法,此時調(diào)用對象聲明的類型是接口類,實際的類型是該接口的某個實現(xiàn)類。假如調(diào)用對象實例是C,查找具體執(zhí)行方法時先在C中查找名稱和描述符都和接口方法一致的方法,如果沒有找到則繼續(xù)在C的父類,父類的父類,不斷往上遞歸查找,直到找到名稱和描述符都和接口方法一致的方法,在編譯正常的情形下通過這兩步查找就可以確認實際被執(zhí)行的方法。
    -invokespecial:調(diào)用實例方法,包括父類方法,私有方法和實例初始化方法三種,實際被執(zhí)行的方法的查找邏輯同invokeinteface基本一樣,都是現(xiàn)在當前類查找,再往上遞歸查找當前類的父類,在編譯期即可確認。
    -invokestatic:調(diào)用靜態(tài)方法,在方法解析成功后,如果方法所在的類或者接口沒有被初始化則指令執(zhí)行時會觸發(fā)其初始化。因為靜態(tài)方法不能被繼承,因此只需在調(diào)用類中查找是否存在目標方法,也是在編譯期即可確認實際被執(zhí)行的方法。
    -invokevirtual:調(diào)用實例方法,依據(jù)調(diào)用對象實例的類型進行分派,如果目標方法是簽名多態(tài)性方法(通常是java.lang.invoke.MethodHanlde的invoke和invokeExact方法),則需要做特殊處理,以保證方法句柄能夠正常調(diào)用。注意invokevirtual并不是其字面描述的一樣的調(diào)用虛方法,調(diào)用某個子類獨有的方法(按C++的虛方法定義這種方法就是非虛方法)也是通過invokevirtual完成,因為JVM無法確認方法調(diào)用實例是該子類的實例,還是該子類的子類實例,后者可能覆寫了該方法。

測試用例如下:

package jni;
 
 
interface InterfaceA{
    void say();
 
    //接口中的default方法只是接口方法的默認實現(xiàn),接口實現(xiàn)類可以改寫默認實現(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();
    }
}

編譯過后執(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)用實例初始化方法
        10: astore_1
        11: aload_1
        12: invokevirtual #13                 // Method say:()V  調(diào)用實例的類型都是類,所以這里都是invokevirtual 
        15: aload_1
        16: invokevirtual #14                 // Method subDo:()V  subDo是子類特有的,為了兼容調(diào)用實例有可能是子類的子類的情形,所以依然使用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)用實例的類型都是接口類,即使該接口方法已經(jīng)提供了默認實現(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指令
該指令的實現(xiàn)參考OpenJDK8 hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp 2522行,源代碼說明如下:

CASE(_invokeinterface): {
        //pc表示當前字節(jié)碼指令的地址,get_native_u2返回從指定地址開始的2字節(jié)的數(shù)據(jù),將其作為short讀取
        //根據(jù)JVM規(guī)范,index表示運行時常量池的索引,指向一個接口方法的符號引用
        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);
 
        //特殊場景下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)用的對象實例
            oop rcvr = STACK_OBJECT(-parms);
            //校驗rcvr是否為空
            VERIFY_OOP(rcvr);
            //獲取rcvr的實際類型Klass
            InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();
            //獲取虛方法表相同索引下的方法
            callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
            //profile統(tǒng)計
            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)用對象實例
        oop rcvr = STACK_OBJECT(-parms);
        CHECK_NULL(rcvr);
        //獲取方法調(diào)用對象實例的真實類型
        InstanceKlass* int2 = (InstanceKlass*) rcvr->klass();
 
        // Receiver subtype check against resolved interface klass (REFC).
        {
          //獲取目標接口方法的resolved interface klass
          Klass* refc = cache->f1_as_klass();
          itableOffsetEntry* scan;
          //遍歷int2實現(xiàn)的所有接口類,判斷是否存在目標接口方法對應(yīng)的resolved interface klass
          for (scan = (itableOffsetEntry*) int2->start_of_itable();
               scan->interface_klass() != NULL;
               scan++) {
            if (scan->interface_klass() == refc) {
              break;
            }
          }
          // int2沒有實現(xiàn)目標resolved interface klass,拋出異常
          if (scan->interface_klass() == NULL) {
            VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
          }
        }
   
       //遍歷int2實現(xiàn)的所有接口類,判斷是否存在目標接口方法對應(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沒有實現(xiàn)目標接口方法的InstanceKlass,拋出異常
        if (i == int2->itable_length()) {
          VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);
        }
 
        //獲取接口方法表的索引
        int mindex = interface_method->itable_index();
        
        //獲取rcvr的接口方法表第一個元素
        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)計
        BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
 
        //執(zhí)行方法調(diào)用
        istate->set_callee(callee);
        istate->set_callee_entry_point(callee->from_interpreted_entry());
 
        //字節(jié)碼指針往后移動5個,_invokeinterface的指令長度是5
        istate->set_bcp_advance(5);
        UPDATE_PC_AND_RETURN(0); // I'll be back...
      }
3、invokevirtual,invokespecial,invokestatic
   invokevirtual,invokespecial,invokestatic三個指令的實現(xiàn)都是一樣的,同樣也是參考bytecodeInterpreter.cpp 2634行,源代碼說明如下:

CASE(_invokevirtual):
      CASE(_invokespecial):
      CASE(_invokestatic): {
        //獲取運行時常量池中目標方法的符號引用的索引
        u2 index = Bytes::get_native_u2(pc+1);
 
        //獲取運行時常量池指定索引的符號引用解析對象
        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);
        {
          Method* callee;
          //如果是invokevirtual指令
          if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) {
            //判斷方法調(diào)用實例對象是否為空
            CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
            //如果是final方法
            if (cache->is_vfinal()) {
              //final方法不需走虛方法表分派,直接使用符號引用解析的結(jié)果
              callee = cache->f2_as_vfinal_method();
              // Profile final方法調(diào)用統(tǒng)計
              BI_PROFILE_UPDATE_FINALCALL();
            } else {
              // get receiver
              int parms = cache->parameter_size();
              //獲取方法調(diào)用的實例對象
              oop rcvr = STACK_OBJECT(-parms);
              VERIFY_OOP(rcvr);
              //獲取方法調(diào)用實例對象的實際klass
              InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();
              //獲取虛方法表中相同索引處的方法
              callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];
              //vitual 調(diào)用 profile統(tǒng)計
              BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());
            }
          } else {
            //invokespecial指令
            if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) {
              //判斷方法調(diào)用實例對象是否為空
              CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));
            }
            //invokespecial指令調(diào)用的方法不需要分派,直接使用符號引用解析的方法
            callee = cache->f1_as_method();
 
            // Profile統(tǒng)計
            BI_PROFILE_UPDATE_CALL();
          }
          //執(zhí)行方法調(diào)用
          istate->set_callee(callee);
          istate->set_callee_entry_point(callee->from_interpreted_entry());
          //字節(jié)碼指針往后移動3個,invokevirtual,invokespecial,invokestatic三個指令的指令長度都是3
          istate->set_bcp_advance(3);
          UPDATE_PC_AND_RETURN(0); // I'll be back...
        }
      }

從上述源碼分析可知,invokevirtual,invokespecial,invokestatic,invokeinteface四個指令在找到了正確的執(zhí)行方法后,就直接通過JVM解釋器跳轉(zhuǎn)到對應(yīng)方法的執(zhí)行了,跟JNI中方法調(diào)用有很大的不同

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容