JNIEnv与JavaVM
JavaVM 是虚拟机在 JNI 层的代表,一个进程只有一个 JavaVM,所有的线程共用一个 JavaVM。
JNIEnv 表示 Java 调用 native 语言的环境,是一个封装了几乎全部 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 只在创建它的线程生效,不能跨线程传递,不同线程的 JNIEnv 彼此独立。
native 环境中创建的线程,如果需要访问 JNI,必须用JavaVM调用 AttachCurrentThread 关联,并使用 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__);
}
}
如何在多线程中管理创建的java对象引用
我们知道java对象的分配和回收是在java虚拟机,而native代码中的内存分配是在native memory。而jni又提供了在native代码中创建和访问java对象的方法,因此如何在native中维护这些java对象成为一个需要关注的问题。比如如何在native多线程中创建并访问同一个java对象,并在合适的时机去销毁?
全局引用Global Reference提供了解决方式,被其引用的对象不会被java虚拟机回收掉。我们可以用一个C++对象去管理这些java对象,在初始化函数中创建这个java对象,在析构函数中销毁和释放掉这些对象。
jni的引用分为局部引用、全局引用和虚引用,具体可以参考https://blog.51cto.com/u_12444109/3026155