• 第48篇native方法调用解释执行的Java方法


    举一个native方法调用解释执行的Java方法的实例,如下:

    public class TestJNI {
        static {
            System.load("/media/mazhi/sourcecode/workspace/projectjava/projectjava01/src/main/java/libdiaoyong.so"); 
        }
        
        public static int getResult() {
        	return 2;
        }
      
        public static native int get();
      
        public static void main(String[] args) {
            TestJNI.get();
        }
    }

    如上实例在main()方法中调用native方法get(),具体的调用过程在前一篇文章中已经详细介绍过。我们这个实例将介绍native方法get()调用Java方法getResult()。

    调用的native方法get()的本地函数的实现如下:

    JNIEXPORT jint JNICALL Java_TestJNI_get(JNIEnv * env, jclass jc){
      jclass cls = (*env)->FindClass(env, "TestJNI");  
      jmethodID id = (*env)->GetStaticMethodID(env, jc, "getResult", "()I");
      jint x = (*env)->CallStaticIntMethod(env,cls, id);
      return x; 
    } 

    在如上的函数中,我们会调用TestJNI类的getResult()方法获取一个整数值,然后本地函数返回这个整数值。

    调用的CallStaticIntMethod()函数的实现如下:

    extern "C" {
     jint  jni_CallStaticIntMethod(JNIEnv *env, jclass cls, jmethodID methodID, ...) {
        JavaThread* thread=JavaThread::thread_from_jni_environment(env);
        ThreadInVMfromNative __tiv(thread);
    
        HandleMarkCleaner __hm(thread);
        Thread* __the_thread__ = thread;
        os::verify_stack_alignment();
        WeakPreserveExceptionMark __wem(thread);
        jint ret = 0;
    
        va_list args;
        __builtin_va_start(args,methodID);
        JavaValue jvalue(T_INT);
        JNI_ArgumentPusherVaArg ap(methodID, args);
        jni_invoke_static(env, &jvalue, 0, JNI_STATIC, methodID, &ap, __the_thread__); 
    
        // ...
    
        __builtin_va_end(args);
        ret = jvalue.get_jint();
        return ret;
     } 
    }

    调用的jni_invoke_static()函数的实现如下:

    // 通过JNI的方式调用Java静态方法
    static void jni_invoke_static(
     JNIEnv              *env,
     JavaValue*          result,
     jobject             receiver,
     JNICallType         call_type,
     jmethodID           method_id,
     JNI_ArgumentPusher  *args,
     TRAPS
    ){
      Method* m = Method::resolve_jmethod_id(method_id);
      methodHandle method(THREAD, m);
    
      ResourceMark rm(THREAD);
      int number_of_parameters = method->size_of_parameters();
      // 这里将要传给Java方法的参数转换为JavaCallArguments实例传下去
      JavaCallArguments  java_args(number_of_parameters);
      args->set_java_argument_object(&java_args);
    
      // 通过方法指纹值填写JavaCallArguments实例
      Fingerprinter fp =  Fingerprinter(method);
      uint64_t x =  fp.fingerprint();
      args->iterate(x);
      // 初始化方法返回类型
      BasicType bt = args->get_ret_type();
      result->set_type(bt);
    
      // 调用java方法
      JavaCalls::call(result, method, &java_args, CHECK);
    
      // 当Java方法返回对象类型数据时,需要句柄化后存储到result中
      if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
         oop tmp =  (oop) result->get_jobject();
         jobject jobj = JNIHandles::make_local(env,tmp);
         result->set_jobject(jobj);
      }
    }

    调用的JavaCalls::call()函数最终会调用到JavaCalls::call_helper()函数,这个函数在之前介绍解释执行Java方法时详细介绍过。当调用到Java方法getResult()时,栈的状态如下图所示。

    其中的蓝色为C/C++函数的栈帧,而绿色为Java方法的栈帧。 

    现在我们要从解释执行的main()方法调用native方法get(),这在前一篇详细介绍过,而从Java_TestJNI_get()函数调用getResult()的过程非常类似于HotSpot VM调用main()方法的过程,关于HotSpot VM调用main()方法的过程在之前详细介绍过,这里不再详细介绍。这里我们介绍一下JavaCallWrapper类。

    通过上图我们能够看出,Java栈和C/C++栈混合在一起,这就为不同类型栈的展开(如GC需要遍历栈帧、异常需要向上查找异常处理器等),不同类型栈帧的转换和适配增加了不少难度,这些我们在后面都会详细介绍。

     公众号 深入剖析Java虚拟机HotSpot 已经更新虚拟机源代码剖析相关文章到60+,欢迎关注,如果有任何问题,可加作者微信mazhimazh,拉你入虚拟机群交流

  • 相关阅读:
    3星|《失败课》:投资人写给创业者的经验谈,有点标题党
    3星|《给你一门人工智能入门生意经》:机器所知胜于其所能言传
    3星|《财经》2018年第5期:西伯利亚冻土层的猛犸象牙是合法的,一根能卖到数万美元
    3星|《增长黑客》:增长黑客是一个牵强的概念
    2星|《只管去做》:做年度计划的入门级介绍,信息浓度太低
    创业者融资过程中需要了解的大坑小坑:《风投的技术》,4星
    4星|吴军《见识》:李开复上级的工作经验、投资经验与人生忠告
    4星|《基因转》:从孟德尔、达尔文到人类胚胎转基因
    3星|《人机平台》:数字化时代的三大类新的再平衡:人脑与机器、产品与平台,以及核心与大众
    3星|《知识的边界》:知识存在于网络中,分歧永远存在
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15747423.html
Copyright © 2020-2023  润新知