JavaVM和JNIEnv的初始化和JVM各模块的初始化都是在JNI_CreateJavaVM()函数中完成。这一篇将详细介绍JavaVM和JNIEnv的初始化过程。

1、初始化JavaVM

JavaVM的初始化都是在JNI_CreateJavaVM()函数中完成,调用链如下:

JavaMain()            java.c
InitializeJVM() java.c
JNI_CreateJavaVM() jni.cpp

在JavaMain()函数中调用InitializeJVM()函数,InitializeJVM()函数会初始化JVM,给JavaVM和JNIEnv变量赋值,通过InvocationFunctions结构体下的CreateJavaVM函数指针来调用对应的函数。在执行LoadJavaVM()函数时,CreateJavaVM函数指针被指向为libjvm.so(在Linux系统上的动态链接库为libjvm.so)动态链接库中JNI_CreateJavaVM()函数。JNI_CreateJavaVM()函数的实现如下:

源代码位置:openjdk/hotspot/src/share/vm/jni.cpp

_JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, void *args) {

  // ...
jint result = JNI_ERR; bool can_try_again = true;
//完成JVM的初始化,如果初始化过程中出现不可恢复的异常则can_try_again会被置为false
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again); //初始化正常
if (result == JNI_OK) {
//获取当前线程,即执行create_vm()函数的线程,也是JNI_CreateJavaVM()函数执行完毕后执行main()方法的线程
JavaThread *thread = JavaThread::current(); // JavaVM赋值,main_vm是jni.h中的全局变量,最张会指向全局变量jni_InvokeInterface
*vm = (JavaVM *)(&main_vm); // vm是方法的参数,是个双重指针 //JNIEnv赋值,从这里也可以看出,JNIEnv其实是线程私有有
*(JNIEnv**)penv = thread->jni_environment(); // ...
}
// ...
return result;
}

调用Threads::create_vm()函数会初始化一系列的JVM模块,如果函数顺利执行完,则函数会返回JNI_OK,表示正确创建出了JVM实例,我们可以给vm赋值,这样JNI的本地函数就可以通过vm来管理这个创建出来的JVM实例了。

使用main_vm来初始化JavaVM类型的变量,main_vm变量的定义如下:

源代码来源:openjdk/hotspot/src/share/vm/jni.cpp

struct JavaVM_ main_vm = {  &jni_InvokeInterface };

JavaVM是JavaVM_的别名,所以我们后面提到的JavaVM指的就是JavaVM​_。同理,JNIEnv是JNIE​nv_的别名。​

jni_InvokeInterface的定义如下:

const struct JNIInvokeInterface_   jni_InvokeInterface = {
NULL,
NULL,
NULL, jni_DestroyJavaVM,
jni_AttachCurrentThread,
jni_DetachCurrentThread,
jni_GetEnv,
jni_AttachCurrentThreadAsDaemon
};

结构体中保存了一系列函数指针,我们大前面说的JNI的本地函数通过vm来管理JVM实例,其实就是通过函数指针来调用对应的函数管理JVM实例。

通过 JavaVM,我们还可以获得 JVMTI 的指针,并获得 JVMTI 函数的使用能力,所有的 JVMTI 函数都通过jvmtiEnv获取,不同的虚拟机实现提供的函数细节可能不一样,但是使用的方式是统一的,关于JVMTI及相关知识这里不介绍,有兴趣的可自行研究。 

2、初始化JNIEnv

JavaMain()                 java.c
InitializeJVM() java.c
JNI_CreateJavaVM() jni.cpp
Threads::create_vm() thread.cpp
JavaThread::JavaThread() thread.cpp
JavaThread::initialize() thread.cpp
jni_functions() jni.cpp

其中调用的Threads:create_vm()函数中会创建JavaThread实例,如下:

JavaThread* main_thread = new JavaThread()

根据调用链可知,在JavaThread构造函数中会调用JavaThread::initialize()函数,最终会调用jni_functions()函数。jni_functions()函数会返回thread.cpp文件中定义的一个全局变量,如下:

struct JNINativeInterface_* jni_functions() {
return &jni_NativeInterface;
}

jni_NativeInterface这个全局变量的定义如下:

struct JNINativeInterface_  jni_NativeInterface = {
NULL,f
NULL,
NULL,
NULL, jni_GetVersion,
jni_DefineClass,
jni_FindClass, ...
};

如上的结构体中保存了各个JNI函数的指针。

调用jni_functions()函数后还会调用如下函数:

void set_jni_functions(struct JNINativeInterface_* functionTable) {
_jni_environment.functions = functionTable;
}

其中_jni_environment变量的定义如下:

源代码位置:openjdk/hotspot/src/share/vm/rumtime/thread.hpp

// 定义在JavaThread中的变量,所以JNIEnv是线程私有的
JNIEnv _jni_environment;

JNIEnv这个结构体定义在之前说过,在这个结构体中首先定义的就是一个指向JNINativeInterface_的指针functions,如下:

struct JNIEnv_ {
const struct JNINativeInterface_ *functions;
...
}

_jni_environment就是main_thread保留的JNIEnv_实例了,正确设置functions属性就表示该实例初始化完成。

为了让大家一目了然,我简单画了一个示意图。

我们将创建出来的JNIEnv实例保存在线程的_jni_environment变量中,这样当线程执行本地函数时,会从这个变量中取出JNIEnv并传递给本地函数,我们得到JNIEnv后就能调用其中的函数来获取JVM内部提供的服务了,例如想要根据类名来查找jclass,可以调用FindClass()函数。

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

  

  

  

  

  

  

第41篇-JNIEnv与JavaVM的初始化的更多相关文章

  1. 第40篇-JNIEnv和JavaVM

    下面介绍2个与JNI机制相关的类型JNIEnv和JavaVM. 1.JNIEnv JNIEnv一般是是由虚拟机传入,而且与线程相关的变量,也就说线程A不能使用线程B的JNIEnv.而作为一个结构体,它 ...

  2. NDK(13)JNIEnv和JavaVM

    转自:  http://www.cnblogs.com/canphp/archive/2012/11/13/2768937.html JNIEnv是一个与线程相关的变量,不同线程的JNIEnv彼此独立 ...

  3. 【Android JNI】JNIEnv和JavaVM的区别

     JNI的实现可涉及两个关键类:JNIEnv和JavaVM. JavaVM:这个代表java的虚拟机.所有的工作都是从获取虚拟机的接口开始的.             第一种方式,在加载动态链接库的时 ...

  4. Jni 线程JNIEnv,JavaVM,JNI_OnLoad(GetEnv返回NULL?FindClass返回NULL?)

    此文章是关于NDK线程的第二篇理论知识笔记.主要有两个点,如下: 1.pthread_create(Too many arguements, expected 1) ?2.线程中如何获取JNIEnv? ...

  5. Java中JNI的使用详解第二篇:JNIEnv类型和jobject类型的解释

    上一篇说的是一个简单的应用,说明JNI是怎么工作的,这一篇主要来说一下,那个本地方法sayHello的参数的说明,以及其中方法的使用 首先来看一下C++中的sayHello方法的实现: JNIEXPO ...

  6. Java中JNI的使用详解第三篇:JNIEnv类型中方法的使用

    转自: http://blog.csdn.net/jiangwei0910410003/article/details/17466369 上一篇说道JNIEnv中的方法的用法,这一篇我们就来通过例子来 ...

  7. spring boot 学习番外篇:超快速项目初始化

    超快速完成 Spring Boot 项目初始化 最近,在浏览 SPRING 官网时,发现一个超级方便的小工具,可以帮助我们快速创建一个 Spring Boot 项目,前提就是你能连接互联网. 依赖 支 ...

  8. 第41篇 推荐一个jekyll博客模板

    本人用的模板是基于Codeboy的博客模板改造模板,(由于本人可能会有很多样式修改,所以不再将修改pullrequst到原项目,在此对codeboy模板表示感谢).功能改造如下: 添加微信支付宝打赏 ...

  9. NDK(5) Android JNI官方综合教程[JavaVM and JNIEnv,Threads ,jclass, jmethodID, and jfieldID,UTF-8 and UTF-16 Strings,Exceptions,Native Libraries等等]

    JNI Tips In this document JavaVM and JNIEnv Threads jclass, jmethodID, and jfieldID Local and Global ...

随机推荐

  1. 讲讲java中线程池的实现

    今天跟一个同学谈到java中的线程池的实现,才发现有些知识点已经记不清了,所以特意把源码打开,对官方文档做了些说明. 其实这些理解了之后,读懂源码应该是没多大问题了,有感兴趣的小伙伴们可以看完说明后自 ...

  2. Coursera Deep Learning笔记 序列模型(二)NLP & Word Embeddings(自然语言处理与词嵌入)

    参考 1. Word Representation 之前介绍用词汇表表示单词,使用one-hot 向量表示词,缺点:它使每个词孤立起来,使得算法对相关词的泛化能力不强. 从上图可以看出相似的单词分布距 ...

  3. Vue报错 type check failed for prop “xxx“. Expected String with value “xx“,got Number with value ‘xx‘

    vue报错    [Vue warn]: Invalid prop: type check failed for prop "name". Expected String with ...

  4. 使用Mybatis的TypeHandler加解密数据

    使用Mybatis的TypeHandler加解密数据 一.背景 二.解决方案 三.需求 四.实现思路 1.编写一个实体类,凡是此实体类的数据都表示需要加解密的 2.编写一个加解密的`TypeHandl ...

  5. 启动Dubbo项目注册Zookeeper时提示zookeeper not connected异常原理解析

    文/朱季谦 遇到一个很诡异的问题,我在启动多个配置相同zookeeper的Dubbo项目时,其他项目都是正常启动,唯独有一个项目在启动过程中,Dubbo注册zookeeper协议时,竟然出现了这样的异 ...

  6. 2021.9.12考试总结[NOIP模拟51]

    T1 茅山道术 仔细观察发现对于每个点只考虑它前面第一个与它颜色相同的点即可. 又仔细观察发现对一段区间染色后以这个区间内点为端点的区间不能染色. 于是对区间右端点而言,区间染色的贡献为遍历到区间左端 ...

  7. 基础篇:JAVA集合,面试专用

    没啥好说的,在座的各位都是靓仔 List 数组 Vector 向量 Stack 栈 Map 映射字典 Set 集合 Queue 队列 Deque 双向队列 关注公众号,一起交流,微信搜一搜: 潜行前行 ...

  8. [个人开源]vue-code-view:一个在线编辑、实时预览的代码交互组件

    组件简介 vue-code-view是一个基于 vue 2.x.轻量级的代码交互组件,在网页中实时编辑运行代码.预览效果的代码交互组件. 使用此组件, 不论 vue 页面还是 Markdown 文档中 ...

  9. linux rtl8188eu ap模式 密码错误 disassoc reason code(8)

    2018-05-30 14:12:46 于深圳南山科技园 最近有个项目,客户需要通过手机app通过机器wifi热点连接,从而实现对机器的设置及视频的实时预览等各种功能.这两天一直在搞rtl8188eu ...

  10. 一步一步学ROP之linux_x64篇(蒸米spark)

    目录 一步一步学ROP之linux_x64篇(蒸米spark) 0x00 序 0x01 Memory Leak & DynELF - 在不获取目标libc.so的情况下进行ROP攻击 0x02 ...