第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,拉你入虛擬機群交流
總結
以上是生活随笔為你收集整理的第41篇JNIEnv与JavaVM的初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux nand 坏块_Nand F
- 下一篇: CCTM_FormElement 类