JNI和字节码方法调用

手机游戏开发者 2024-9-21 10:12:35 101 0 来自 中国
Java对象创建的本质就是按照对象的大小分配一块内存,然后完成属性的初始化。对象创建完了,接着干啥了?调用Java方法完成特定功能。这就是我们接下来探究的主题,Java方法调用是怎么实现的。
一、Main方法
main方法是Java应用启动实行的入口方法,这个方法是怎么实行的了?,关键代码在OpenJDK jdk/src/share/bin/java.c中的int JNICALL JavaMain(void * _args)方法,如下图:
1.png 即main方法是通过JNI的CallStaticVoidMethod方法实行的。
二、JNI 方法调用
1、API定义
JNI的方法调用的API,分为三种,总结如下:

  • NativeType Call<type>Method:实行非静态方法调用,假如子类覆写父类方法,则调用子类覆写后的方法。
  • NativeType CallNonvirtual<type>Method:Call<type>Method的扩展版,假如子类覆写父类方法,可以根据jmethedId调用子类覆写后的方法大概调用父类原来的方法,前者jmethodId从子类jclass中获取,后者jmethodId从父类jclass中获取。
  • NativeType CallStatic<type>Method:实行静态方法调用。
三者根据通报参数的方式的差异,又有三个“重载”方法,以Call<type>Method为例:

  • Call<type>Method:按照Java方法中定义的参数范例和序次依次通报参数即可,注意JNI中不支持根本范例的主动装箱拆箱,假如方法参数是包装类,则必要调用包装类的构造方法构造根本包装类实例。
  • Call<type>MethodA:按照Java方法中定义的参数范例和序次将全部参数放入一个jvalue数组中,然后传入该数组的指针即可,jvalue是一个union范例,可以支持全部的参数范例。
    Call<type>MethodV:按照Java方法中定义的参数范例和序次将全部参数放入一个va_list类中,该类体现一个参数列表。
    通过宏的方式实现差异NativeType和type下的方法定义,以Call<type>Method为例,如下图:
全部方法调用的API终极调用的都是jni.cpp中jni_invoke_static和jni_invoke_nonstatic方法,这两个方法的调用如下图:
此中jni_NewObject三个方法是通过jni_invoke_nonstatic调用类构造方法
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转换成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);  //获取方法参数个数  int number_of_parameters = method->size_of_parameters();  //初始化java_args  JavaCallArguments java_args(number_of_parameters);  args->set_java_argument_object(&java_args);  //校验目标方法为静态方法  assert(method->is_static(), "method should be static");   //fingerprint返回目标方法的fingerprint,是一长串数字,包罗方法的参数范例,返回值范例等信息  /剖析方法参数到JavaCallArguments中  args->iterate( Fingerprinter(method).fingerprint() );  // 设置返回效果的效果范例  result->set_type(args->get_ret_type());   //实行方法调用  JavaCalls::call(result, method, &java_args, CHECK);   // 假如效果范例是对象大概数组范例,则必要将其转换资源地引用  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, //静态方法  JNI_VIRTUAL, //虚方法  JNI_NONVIRTUAL //非虚方法};   inline static Method* resolve_jmethod_id(jmethodID mid) {    assert(mid != NULL, "JNI method id should not be null");    //据此可知,jmethodID现实是Method的指针的指针    return *((Method**)mid);  }3、jni_invoke_nonstaticstatic void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {  //校验调用实例方法关联的对象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转换成Method*    Method* m = Method::resolve_jmethod_id(method_id);    //获取方法个数    number_of_parameters = m->size_of_parameters();    //获取此方法所属的类Klass    Klass* holder = m->method_holder();    if (call_type != JNI_VIRTUAL) {        //假如好坏虚方法调用,即CallNonvirtual<type>Method,则利用指定的方法        //此时jmethodID从父类Klass获取的则利用父类的实现,假如利用子类Klass的实现则利用子类的实现        selected_method = m;    }     //虚方法调用,即Call<type>Method,itable即接口方法表    else if (!m->has_itable_index()) {      // non-interface call -- for that little speed boost, don't handlize      // 非接口方法调用      debug_only(No_Safepoint_Verifier nosafepoint;)      //校验该方法在虚方法表的索引是否有效,假如目标类已经完成链接和初始化则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分发,即父类定义的final方法      if (vtbl_index != Method::nonvirtual_vtable_index) {        //获取receiver对应的Klass        Klass* k = h_recv->klass();        InstanceKlass *ik = (InstanceKlass*)k;        //获取目标Klass在指定虚方法表索引处的虚方法实现,        //如receiver现实是子类实例,jmethodID无论从父类Klass照旧子类Klass获取的,现实调用的都是子类的实现        selected_method = ik->method_at_vtable(vtbl_index);      } else {        //final方法        selected_method = m;      }    } else {      //接口方法      KlassHandle h_holder(THREAD, holder);      //获取接口方法表中的索引,无论jmethodID从接口类Klass照旧实现类Klass获取的,其itbl_index都是一样的      int itbl_index = m->itable_index();      Klass* k = h_recv->klass();      //获取接口方法,利用receiver实例现实的类的接口实现      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);   //校验方法不是静态方法  assert(!method->is_static(), "method should not be static");  //设置继续方法调用的对象实例  args->push_receiver(h_recv); // Push jobject handle   //剖析方法参数  args->iterate( Fingerprinter(method).fingerprint() );  //设置方法返回范例  result->set_type(args->get_ret_type());   //调用方法  JavaCalls::call(result, method, &java_args, CHECK);   //处置惩罚效果返回值  if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {    result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));  }}从上述实现可知,jni_invoke_nonstatic比jni_invoke_static就是多了确认现实调用方法的逻辑。CallNonvirtual<type>Method时和CallStatic<type>Method现实逻辑是一样的,都是实行直接实行传入的jmethodID对应的Java方法,而Call<type>Method必要根据传入的jmethodID确认目标方法的虚方法表索引大概接口方法表索引,然后根据索引获取目标实例对象所属的类的实现方法。itable和vtable可参考《Hotspot Klass模型——Java类内存体现机制》。
三、方法调用字节码指令
1、指令定义
main方法通过JNI的方法调用开始实行后,假如调用其他的Java方法就必须通过方法调用的字节码指令,JNI的方法调用仅限于本地方法实现利用。实行方法调用的字节码指令总共有5个,概述如下,详情可以参考《Java假造机规范》:

  • invokedynamic:调用动态方法,动态方法是指终极被调用的方法可以在运行期由程序动态指定,从JDK7引入,但是JDK7的编译器无法直接利用该指令,只能通过ASM字节码编辑工具调用。现在重要用于JDK8中java.lang.invoke包和Lambda表达式。
    -invokeinteface:调用接口方法,即由接口类中定义的方法,此时调用对象声明的范例是接口类,现实的范例是该接口的某个实现类。假如调用对象实例是C,查找具体实行方法时先在C中查找名称和形貌符都和接口方法划一的方法,假如没有找到则继续在C的父类,父类的父类,不绝往上递归查找,直到找到名称和形貌符都和接口方法划一的方法,在编译正常的情形下通过这两步查找就可以确认现实被实行的方法。
    -invokespecial:调用实例方法,包罗父类方法,私有方法和实例初始化方法三种,现实被实行的方法的查找逻辑同invokeinteface根本一样,都是现在当前类查找,再往上递归查找当前类的父类,在编译期即可确认。
    -invokestatic:调用静态方法,在方法剖析乐成后,假如方法地点的类大概接口没有被初始化则指令实行时会触发其初始化。由于静态方法不能被继续,因此只需在调用类中查找是否存在目标方法,也是在编译期即可确认现实被实行的方法。
    -invokevirtual:调用实例方法,依据调用对象实例的范例进行分派,假如目标方法是署名多态性方法(通常是java.lang.invoke.MethodHanlde的invoke和invokeExact方法),则必要做特殊处置惩罚,以包管方法句柄可以大概正常调用。注意invokevirtual并不是其字面形貌的一样的调用虚方法,调用某个子类独有的方法(按C++的虚方法定义这种方法就好坏虚方法)也是通过invokevirtual完成,由于JVM无法确认方法调用实例是该子类的实例,照旧该子类的子类实例,后者大概覆写了该方法。
测试用比方下:
package jni;  interface InterfaceA{    void say();     //接口中的default方法只是接口方法的默认实现,接口实现类可以改写默认实现    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();    }}编译过后实行javap -v可以查察具体的字节码指令,截取部门如下:
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  调用父类方法         4: getstatic     #3                  // Field java/lang/System.outjava/io/PrintStream;         7: ldc           #4                  // String InvokeTest say         9: invokevirtual #5                  // Method java/io/PrintStream.printlnLjava/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  调用私有方法         4: getstatic     #3                  // Field java/lang/System.outjava/io/PrintStream;         7: ldc           #8                  // String InvokeTest subDo         9: invokevirtual #5                  // Method java/io/PrintStream.printlnLjava/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   调用静态方法         3: new           #11                 // class jni/InvokeTest         6: dup         7: invokespecial #12                 // Method "<init>")V  调用实例初始化方法        10: astore_1        11: aload_1        12: invokevirtual #13                 // Method say)V  调用实例的范例都是类,所以这里都是invokevirtual         15: aload_1        16: invokevirtual #14                 // Method subDo)V  subDo是子类特有的,为了兼容调用实例有大概是子类的子类的情形,所以依然利用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  调用实例的范例都是接口类,纵然该接口方法已经提供了默认实现,依然利用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: return2、invokeinteface指令
该指令的实现参考OpenJDK8 hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp 2522行,源代码分析如下:
CASE(_invokeinterface): {        //pc体现当前字节码指令的地点,get_native_u2返回从指定地点开始的2字节的数据,将其作为short读取        //根据JVM规范,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的虚方法调用,        if (cache->is_forced_virtual()) {          Method* callee;          //检查操纵数栈的方法参数是否为空          CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));          //假如是final方法          if (cache->is_vfinal()) {            //获取剖析后的方法            callee = cache->f2_as_vfinal_method();            //final方法调用            BI_PROFILE_UPDATE_FINALCALL();          } else {             //非final方法            // Get receiver.            int parms = cache->parameter_size();            //获取实行方法调用的对象实例            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统计            BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());          }          //调用方法          istate->set_callee(callee);          istate->set_callee_entry_point(callee->from_interpreted_entry());           istate->set_bcp_advance(5);          //方法调用完成返回          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();        //获取方法调用对象实例        oop rcvr = STACK_OBJECT(-parms);        CHECK_NULL(rcvr);        //获取方法调用对象实例的真实范例        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实现的全部接口类,判定是否存在目标接口方法对应的resolved interface klass          for (scan = (itableOffsetEntry*) int2->start_of_itable();               scan->interface_klass() != NULL;               scan++) {            if (scan->interface_klass() == refc) {              break;            }          }          // int2没有实现目标resolved interface klass,抛出非常          if (scan->interface_klass() == NULL) {            VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), "", note_no_trap);          }        }          //遍历int2实现的全部接口类,判定是否存在目标接口方法对应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没有实现目标接口方法的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统计        BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());         //实行方法调用        istate->set_callee(callee);        istate->set_callee_entry_point(callee->from_interpreted_entry());         //字节码指针以后移动5个,_invokeinterface的指令长度是5        istate->set_bcp_advance(5);        UPDATE_PC_AND_RETURN(0); // I'll be back...      }3、invokevirtual,invokespecial,invokestatic   invokevirtual,invokespecial,invokestatic三个指令的实现都是一样的,同样也是参考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) {            //判定方法调用实例对象是否为空            CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));            //假如是final方法            if (cache->is_vfinal()) {              //final方法不需走虚方法表分派,直接利用符号引用剖析的效果              callee = cache->f2_as_vfinal_method();              // Profile final方法调用统计              BI_PROFILE_UPDATE_FINALCALL();            } else {              // get receiver              int parms = cache->parameter_size();              //获取方法调用的实例对象              oop rcvr = STACK_OBJECT(-parms);              VERIFY_OOP(rcvr);              //获取方法调用实例对象的现实klass              InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass();              //获取虚方法表中类似索引处的方法              callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()];              //vitual 调用 profile统计              BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass());            }          } else {            //invokespecial指令            if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) {              //判定方法调用实例对象是否为空              CHECK_NULL(STACK_OBJECT(-(cache->parameter_size())));            }            //invokespecial指令调用的方法不必要分派,直接利用符号引用剖析的方法            callee = cache->f1_as_method();             // Profile统计            BI_PROFILE_UPDATE_CALL();          }          //实行方法调用          istate->set_callee(callee);          istate->set_callee_entry_point(callee->from_interpreted_entry());          //字节码指针以后移动3个,invokevirtual,invokespecial,invokestatic三个指令的指令长度都是3          istate->set_bcp_advance(3);          UPDATE_PC_AND_RETURN(0); // I'll be back...        }      }从上述源码分析可知,invokevirtual,invokespecial,invokestatic,invokeinteface四个指令在找到了精确的实行方法后,就直接通过JVM表明器跳转到对应方法的实行了,跟JNI中方法调用有很大的差异
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 12:00, Processed in 0.151135 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表