zoukankan      html  css  js  c++  java
  • 第41篇JNIEnv与JavaVM的初始化

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

      

      

      

      

      

      

  • 相关阅读:
    IDEA与tomcat的相关配置
    传统项目IDEA集成tomcat配置并创建web项目
    Maven项目更改工程运行环境插件
    Maven项目使用骨架或不使用骨架创建工程
    Maven项目中jar包冲突问题解决 导入jar包scope作用域的使用
    分开使用 Celery beat and worker
    (转) -- 线程、进程、协程
    Django REST Framework -- REST API 报错:403
    postman -- 循环调用RESTAPI
    (转载) -- ab(Apache benchmark) 一款常压力测试工具
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15631697.html
Copyright © 2011-2022 走看看