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

第48篇-native方法调用解释执行的Java方法的更多相关文章

  1. 第47篇-解释执行的Java方法调用native方法小实例

    举个小实例,如下: public class TestJNI { static { // 程序在加载时,自动加载libdiaoyong.so库 System.loadLibrary("dia ...

  2. 第44篇-为native方法设置解释执行入口

    对于Java中的native方法来说,实际上调用的是C/C++实现的本地函数,由于可能会在Java解释执行过程中调用native方法,或在本地函数的实现过程中调用Java方法,所以当两者相互调用时,必 ...

  3. 意外作出了一个javascript的服务器,可以通过js调用并执行任何java(包括 所有java 内核基本库)及C#类库,并最终由 C# 执行你提交的javascript代码! 不敢藏私,特与大家分

    最近研发BDC 云开发部署平台的数据路由及服务管理器意外作出了一个javascript的服务器,可以通过js调用并执行任何java(包括 所有java 内核基本库)及C#类库,并最终由 C# 执行你提 ...

  4. static 关键字详解 static方法调用非static属性和方法

    静态的属性和方法在内存中的存放地址与非静态的是不同的,静态的是存放在static区,它意味着静态方法是没有this的,所以我们不可以从一个static方法内部发出对非static方法的调用.但是反之是 ...

  5. 生命周期方法调用,以及在onStop()方法中处理草稿信息

    生命周期方法调用顺序 1. 从会话列表界面跳转到信息列表界面. 07-17 17:29:18.718: I/txrjsms(19370): MessageListActivity.onCreate 0 ...

  6. Java方法调用机制

    最近在编程时,修改方法传入对象的对象引用,并没有将修改反映到调用方法中.奇怪为什么结果没有变化,原因是遗忘了Java对象引用和内存分配机制.本文介绍3个点: ① 该问题举例说明 ② 简要阐述Java内 ...

  7. 04 JVM是如何执行方法调用的(下)

    虚方法调用 Java 里所有非私有实例方法调用都会被编译成 invokevirtual 指令,而接口方法调用会被编译成 invokeinterface 指令.这两种指令,均属于 Java 虚拟机中的虚 ...

  8. 第5篇-调用Java方法后弹出栈帧及处理返回结果

    在前一篇 第4篇-JVM终于开始调用Java主类的main()方法啦 介绍了通过callq调用entry point,不过我们并没有看完generate_call_stub()函数的实现.接下来在ge ...

  9. Struts2学习笔记 - Action篇<动态方法调用>

    有三种方法可以使一个Action处理多个请求 动态方法调用DMI 定义逻辑Acton 在配置文件中使用通配符 这里就说一下Dynamic Method nvocation ,动态方法调用,什么是动态方 ...

随机推荐

  1. day05 连表查询与子查询

    day05 连表查询与子查询 昨日内容回顾 表关系之一对一 换位思考之后得出两边都是不可以 要么是没有关系,要么是一对一 一对一的表关系外键虽然建在哪个都可以,但是建议建在查询频率多的表上 # 外键其 ...

  2. Scala(八)【面向对象总结】

    面向对象总结 面向对象 1.scala包 1.声明包 1.在文件第一行通过package 包名 2.package 包名{ .... } 第二种方法,包名只能在target目录才能看到 2.导入包 1 ...

  3. 『学了就忘』Linux启动引导与修复 — 69、启动引导程序(grub)

    目录 1.启动引导程序(Boot Loader)简介 2.启动引导程序grub的作用 3.启动引导程序grub的位置 4./grub目录中其他的文件简单介绍 提示: 简单地说,Boot Loader就 ...

  4. Linux定时任务crontable简介

    Linux下定时执行任务的方法:Linux之crond 服务介绍:https://www.cnblogs.com/liang-io/p/9596294.html http://www.mamicode ...

  5. 关于form表单提交ajaxForm和ajaxSubmit的用法与区别

    前几天在学习form表单提交时看到这两种方法,这两种方法都是实现form的ajax提交的方法,看了很多资料还是不太明白其用法和区别,最后直接自己写demo,很快就理解,所以说实操是学习的最快捷直接的途 ...

  6. 接口测试 python+PyCharm 环境搭建

    1.配置Python环境变量 a:我的电脑->属性->高级系统设置->环境变量->系统变量中的PATH变量. 变量名:PATH      修改变量值为:;C:\Python27 ...

  7. Spring组合注解与元注解

    目录 注解说明 源代码 使用范例 注解说明 元注解:可以注解到别的注解上的注解,所以元注解首先基于条件@Target({ElementType.TYPE}) ,目标使用在类文件上 . 组合注解:连个元 ...

  8. C# SAP Connector .NET Framework 4.5 版本下载

    公司对接 SAP 数据使用 SAP Connector 程序,主要是两个类库:sapnco.dll.sapnco_utils.dll 但是没想到它的版本如此混乱,.NET 2.0 和 .NET 4.0 ...

  9. 一文详解TDSQL PG版Oracle兼容性实践

    TDSQL PG版分布式关系型数据库,是一款同时面向在线事务交易和MPP实时数据分析的高性能HTAP数据库系统.面对应用业务产生的不定性数据爆炸需求,不管是高并发交易还是海量实时数据分析,TDSQL ...

  10. python进程管理工具Supervisor

    一.Supervisor简单介绍 supervisor是一个 Client/Server模式的系统,允许用户在类unix操作系统上监视和控制多个进程,或者可以说是多个程序.supervisor与lau ...