zoukankan      html  css  js  c++  java
  • 第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,拉你入虚拟机群交流

  • 相关阅读:
    hihocoder 1049 后序遍历
    hihocoder 1310 岛屿
    Leetcode 63. Unique Paths II
    Leetcode 62. Unique Paths
    Leetcode 70. Climbing Stairs
    poj 3544 Journey with Pigs
    Leetcode 338. Counting Bits
    Leetcode 136. Single Number
    Leetcode 342. Power of Four
    Leetcode 299. Bulls and Cows
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15747423.html
Copyright © 2011-2022 走看看