举一个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,拉你入虚拟机群交流