NDK开发-内存管理
目录
局部引用
大多数的jni函数,调用以后返回的结果都是局部引用。局部引用在所在函数执行完毕后就会销毁对应的内存空间,和常说的局部变量有区别,赋值给全局变量也没用,局部引用无法跨函数使用。
删除局部引用
一个函数内的局部引用数量是有限制的,在早期的安卓系统中,体现的更为明显。当函数体内需要大量使用局部引用时,比如大循环,最好及时删除不用的局部引用,可以使用env->DeleteLocalRef来删除局部引用。用法如下:
1 2 3 4 5 6
| jstring javaString = env->NewStringUTF("Hello from JNI"); const char *str = env->GetStringUTFChars(javaString, NULL); LOGE("局部引用javastring删除前 is %s",str) env->DeleteLocalRef(javaString); const char *str1 = env->GetStringUTFChars(javaString, NULL); LOGE("局部引用javastring删除后 is %s",str1)
|
- 创建局部引用:调用
env->NewStringUTF("Hello from JNI")
创建一个新的Java字符串对象,并返回一个本地引用 javaString
。
- 打印引用前的内容和地址:使用
env->GetStringUTFChars
获取字符串内容,并打印出来。
- 删除本地引用:调用
env->DeleteLocalRef(javaString)
删除本地引用。
- 尝试访问已删除的引用:尝试再次访问
javaString
,这通常会导致未定义行为(例如崩溃或异常),因为引用已被删除。
通过这个示例,你可以看到删除局部引用前后引用地址和内容的变化,以及尝试访问已删除引用时可能出现的问题。这样可以更好地理解局部引用的概念及其在JNI中的作用。
判断可用局部引用
env->EnsureLocalCapacity(num),用于判断是否有足够的局部引用可以使用,足够则返回0。
1 2
| LOGI("可用局部引用数量1000000结果 :%d",env->EnsureLocalCapacity(1000000)); LOGI("可用局部引用数量10000000结果 :%d",env->EnsureLocalCapacity(10000000));
|
批量管理局部引用
需大量使用局部引用时,手动删除太过麻烦,可以使用以下两个函数来批量管理局部引用。
env->PushLocalFrame(num)
: 创建一个新的局部引用帧,指定可以创建的局部引用的最大数量 num
。这个帧将包含在调用 PopLocalFrame
之前创建的所有局部引用。
env->PopLocalFrame(result)
: 销毁当前的局部引用帧,释放该帧中所有局部引用。如果 result
参数不为 nullptr
,则将其作为返回值返回给调用者,并将其添加到前一个局部引用帧中。
- 使用
env->PopLocalFrame(nullptr)
弹出当前的局部引用帧,并释放所有在这个帧中创建的局部引用。
- 如果需要保留某个局部引用,可以将其作为参数传递给
PopLocalFrame
,例如 env->PopLocalFrame(保留的引用)
。
释放所有局部引用
1 2 3 4 5 6 7 8
| jstring javaString; env->PushLocalFrame(2); for(int i = 0;i < 2;i++){ javaString = env->NewStringUTF("Hello from JNI"); } LOGI("str is %s",env->GetStringUTFChars(javaString, NULL)) jstring result = static_cast<jstring>(env->PopLocalFrame(nullptr)); LOGI("str1 is %p",result)
|
保留局部引用释放
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| jstring javaString;
env->PushLocalFrame(2);
for(int i = 0;i < 2;i++){
javaString = env->NewStringUTF("Hello from JNI");
}
LOGI("str is %s",env->GetStringUTFChars(javaString, NULL))
jstring result = static_cast<jstring>(env->PopLocalFrame(javaString));
LOGI("str1 is %p",result)
LOGI("str1 is %s",env->GetStringUTFChars(result, NULL))
|
全局引用
在jni开发中,需要跨函数使用变量时,直接定义全局变量时没用的,需要使用以下两个方法,来创建和删除全局引用
- env->NewGlobalRef()
- env->DeleteGlobalRef()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <jni.h> #include <string> #include <android/log.h>
#define TAG "hackfun" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO , TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
jclass clazz = nullptr;
extern "C" JNIEXPORT jstring JNICALL Java_com_wnxwzr_sotest1_MainActivity_stringFromJNI(JNIEnv *env, jobject ) { std::string hello = "Hello from C++";
if (clazz == nullptr) { LOGE("Global reference is null"); } else { LOGI("Global reference is valid - %p", clazz); }
return env->NewStringUTF(hello.c_str()); }
extern "C" JNIEXPORT void JNICALL Java_com_wnxwzr_sotest1_MainActivity_accessFields(JNIEnv *env, jobject, jobject ndkdemoObj) {
jclass tmpclazz = env->FindClass("com/wnxwzr/sotest1/NdkDemo"); if (tmpclazz == nullptr) { LOGE("FindClass failed"); } else { LOGI("FindClass succeeded"); }
clazz = static_cast<jclass>(env->NewGlobalRef(tmpclazz)); if (clazz == nullptr) { LOGE("NewGlobalRef failed"); } else { LOGI("NewGlobalRef succeeded"); }
env->DeleteLocalRef(tmpclazz); }
|
弱全局引用
与全局引用基本相同,区别是弱全局引用有可能会被回收
- env->NewWeakGlobalRef();
- env->DeleteWeakGlobalRef();