JNIEnv與JavaVM
JavaVM 是虛擬機在 JNI 層的代表,一個進程只有一個 JavaVM儿礼,所有的線程共用一個 JavaVM印衔。
JNIEnv 表示 Java 調(diào)用 native 語言的環(huán)境瞒窒,是一個封裝了幾乎全部 JNI 方法的指針但骨。
其中 JavaVM 是一個全局變量躲惰,一個進程只有一個 JavaVM 對象致份。而 JNIEnv 是一個線程擁有一個,不同線程的 JNIEnv 彼此獨立础拨。
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
struct JNINativeInterface {
...
jint (*GetVersion)(JNIEnv *);
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);
...
};
struct JNIInvokeInterface {
void* reserved0;
void* reserved1;
void* reserved2;
jint (*DestroyJavaVM)(JavaVM*);
jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
jint (*DetachCurrentThread)(JavaVM*);
jint (*GetEnv)(JavaVM*, void**, jint);
jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};
如何在多線程中獲取JNIEnv
JNIEnv 只在創(chuàng)建它的線程生效氮块,不能跨線程傳遞,不同線程的 JNIEnv 彼此獨立诡宗。
native 環(huán)境中創(chuàng)建的線程滔蝉,如果需要訪問 JNI,必須用JavaVM調(diào)用 AttachCurrentThread 關聯(lián)塔沃,并使用 DetachCurrentThread 解除鏈接蝠引。
static JavaVM *ms2_vm = NULL;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
ms2_vm = vm;
return JNI_VERSION_1_4;
}
bool get_env(JNIEnv ** env) {
int status = ms2_vm->GetEnv((void**) env, JNI_VERSION_1_4);
if (status != JNI_OK) {
status = ms2_vm->AttachCurrentThread(env, NULL);
if(status != JNI_OK){
ehome_printf("[%s]FAILED\n", __FUNCTION__);
return false;
}
ehome_printf("[%s]SUCCESS\n", __FUNCTION__);
}else{
ehome_printf("[%s]Attach aready\n", __FUNCTION__);
}
return true;
}
void release_env(void) {
JNIEnv *env ;
int status = ms2_vm->GetEnv((void**)&env, JNI_VERSION_1_4);
if (status == JNI_OK) {
ehome_printf("[%s]getpid=%d, gettid=%d\n", __FUNCTION__, getpid(),gettid());
ms2_vm->DetachCurrentThread();
}else{
ehome_printf("[%s]NEED NOT DETACH\n", __FUNCTION__);
}
}
如何在多線程中管理創(chuàng)建的java對象引用
我們知道java對象的分配和回收是在java虛擬機,而native代碼中的內(nèi)存分配是在native memory。而jni又提供了在native代碼中創(chuàng)建和訪問java對象的方法螃概,因此如何在native中維護這些java對象成為一個需要關注的問題矫夯。比如如何在native多線程中創(chuàng)建并訪問同一個java對象,并在合適的時機去銷毀吊洼?
全局引用Global Reference提供了解決方式训貌,被其引用的對象不會被java虛擬機回收掉。我們可以用一個C++對象去管理這些java對象融蹂,在初始化函數(shù)中創(chuàng)建這個java對象,在析構(gòu)函數(shù)中銷毀和釋放掉這些對象弄企。
jni的引用分為局部引用超燃、全局引用和虛引用,具體可以參考https://blog.51cto.com/u_12444109/3026155