赞
踩
错误❌:没有正确释放,会导致内存泄漏
const char *str = env->GetStringUTFChars(jstr, nullptr);
正确✅:必须调用 ReleaseStringUTFChars 释放
- const char *str = env->GetStringUTFChars(jstr, nullptr);
- // TODO use str
- env->ReleaseStringUTFChars(jstr, str);
错误❌:Release 之后就不能再使用
- const char *str = env->GetStringUTFChars(jstr, nullptr);
- env->ReleaseStringUTFChars(jstr, str);
- // TODO use str
正确✅:可以把 char* 转换成 std::string再使用
- const char *c_str = env->GetStringUTFChars(jstr, nullptr);
- std::string str(c_str, env->GetStringLength(jstr));
- env->ReleaseStringUTFChars(jstr, c_str);
- // TODO use str
正确✅:自己分配空间,自己进行 delete 释放。如果数据不大,推荐先转换成 std::string。
- int size = env->GetStringLength(jstr);
- char *c_arr = new char[size];
- env->GetStringRegion(jstr, 0, size, (jchar *) c_arr);
- // TODO use c_arr
- delete[] c_arr;
错误❌:没有正确释放,会导致内存泄漏
auto int_arr = env->GetIntArrayElements(jint_arr, nullptr);
正确✅
- auto int_arr = env->GetIntArrayElements(jint_arr, nullptr);
- // TODO use int_arr
- env->ReleaseIntArrayElements(jint_arr, int_arr, 0);
错误❌:不能在 Release 之后使用,会导致野指针。
- auto int_arr = env->GetIntArrayElements(jint_arr, nullptr);
- env->ReleaseIntArrayElements(jint_arr, int_arr, 0);
- // TODO use int_arr
正确✅:如果需要在 Release 之后使用,那么就要自行分配内存,然后拷贝,但是自己分配的内存也要释放。
- int size = env->GetArrayLength(jint_arr);
- auto int_arr = env->GetIntArrayElements(jint_arr, nullptr);
- int *int_arr2 = new int[size];
- memcpy(int_arr2, int_arr, sizeof(int) * size);
- env->ReleaseIntArrayElements(jint_arr, int_arr, 0);
-
- // TODO use int_arr
- delete [] int_arr2;
正确✅:自己分配内存,使用 GetIntArrayRegion 拷贝数据,无需调用 ReleaseIntArrayElements 函数。但是也要注意释放自己分配的空间。
- int size = env->GetArrayLength(jint_arr);
- int *int_arr = new int[size];
- env->GetIntArrayRegion(jint_arr, 0, size, int_arr);
-
- // TODO use int_arr
- delete[] int_arr;
GetStringUTFChars 和 GetStringRegion,GetByteArrayElements 和 GetByteArrayRegion 区别
GetStringUTFChars 和 GetByteArrayElements 函数都是jni帮我们分配内存然后拷贝jvm对象的信息到native层,需要通知jni去释放内存。而 GetStringRegion 和 GetByteArrayRegion 是我们自己分配好内存,然后把指针传给jni,jni往指针写入数据,所以不需要jni去释放内存,但是我们自己分配的空间不需要使用后必须释放。
GetByteArrayElements 获取的数据,执行ReleaseByteArrayElements后是否还能使用?
不能,会导致出现野指针。刚Release就使用看起来是正常,实际上是因为内存的数据没有马上被抹除,一段时间后再使用就会出现出现野指针问题。
仅允许在ReleaseByteArrayElements之前使用,如果需要使用可以自己分配内存把内存拷贝出来,但是要注意,要自己销毁。如果需要离开jni函数后继续使用数据,推荐使用GetByteArrayRegion。
GetByteArrayElements/ReleaseByteArrayElements 的数据,是否可以手动 delete 销毁?
不能,由于已经被销毁,不能重复销毁。
JNIEnv 是否可以复用
JNIEnv 只允许在相同的线程上使用,如果需要使用JNIEnv,可以在 JNI_OnLoad 函数把 JavaVM 保存下来,在使用的时候去创建 JNIEnv。这种情况同时是在 native 异步调用 Java 对象的时候使用。
- JavaVM *jvm = nullptr;
-
- JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
- JNIEnv *env = nullptr;
- if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
- return JNI_ERR;
- }
- jvm = vm;
- return JNI_VERSION_1_6;
- }
-
- void GetJNIEnv(JNIEnv *&env) {
- int status = jvm->GetEnv((void **) &env, JNI_VERSION_1_6);
- // 获取当前native线程是否有没有被附加到jvm环境中
- if (status == JNI_EDETACHED) {
- // 如果没有, 主动附加到jvm环境中,获取到env
- if (jvm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
- // Failed to attach
- }
- } else if (status == JNI_OK) {
- // success
- } else if (status == JNI_EVERSION) {
- // GetEnv: version not supported
- }
- }
-
- void test() {
- JNIEnv *env;
- GetJNIEnv(env);
- jstring jstr = env->NewStringUTF("hello world");
- }
- JNI DETECTED ERROR IN APPLICATION: JNI NewGlobalRef called with pending exception java.lang.ClassNotFoundException: Didn't find class "com.cross.Cross" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/system/lib64, /system_ext/lib64, /system/lib64, /system_ext/lib64]]
- java_vm_ext.cc:577] at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:207)
- java_vm_ext.cc:577] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:379)
- java_vm_ext.cc:577] at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
- java_vm_ext.cc:577]
- java_vm_ext.cc:577] in call to NewGlobalRef
如果在 C++ 创建子线程,通过 AttachCurrentThread 获取到 JNIEnv,调用 FindClass 是会报错的,通常而言,如果需要 FindClass ,尽量在 JNI_OnLoad 去创建一个全局的变量。这是因为通过AttachCurrentThread附加到虚拟机的线程在查找类时只会通过系统类加载器进行查找,不会通过应用类加载器进行查找,因此可以加载系统类,但是不能加载非系统类。
- JavaVM *jvm = nullptr;
- jclass cross_class;
-
- JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
- JNIEnv *env = nullptr;
- if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
- return JNI_ERR;
- }
- cross_class = (jclass) env->NewGlobalRef(env->FindClass("com/cross/Cross"));
- jvm = vm;
- return JNI_VERSION_1_6;
- }
作者:ImWiki
链接:https://www.jianshu.com/p/5cde114159d4
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。