天天看點

android jni 釋放資源

今天,簡單講講android 如何釋放在jni建立得 結構體等資源。

因為android裡是自動釋放資源的,是以之前沒有注意這一點,後來查找資料才發現jni需要自己釋放資源。這裡記錄一下。

JNI 程式設計實作了 native code 和 Java 程式的互動,是以 JNI 代碼程式設計既遵循 native code 程式設計語言的程式設計規則,同時也遵守 JNI 程式設計的文檔規範。在記憶體管理方面,native code 程式設計語言本身的記憶體管理機制依然要遵循,同時也要考慮 JNI 程式設計的記憶體管理。

本章簡單概括 JNI 程式設計中顯而易見的記憶體洩漏。從 native code 程式設計語言自身的記憶體管理,和 JNI 規範附加的記憶體管理兩方面進行闡述。

Native Code 本身的記憶體洩漏

JNI 程式設計首先是一門具體的程式設計語言,或者 C 語言,或者 C++,或者彙編,或者其它 native 的程式設計語言。每門程式設計語言環境都實作了自身的記憶體管理機制。是以,JNI 程式開發者要遵循 native 語言本身的記憶體管理機制,避免造成記憶體洩漏。以 C 語言為例,當用 malloc() 在程序堆中動态配置設定記憶體時,JNI 程式在使用完後,應當調用 free() 将記憶體釋放。總之,所有在 native 語言程式設計中應當注意的記憶體洩漏規則,在 JNI 程式設計中依然适應。

Native 語言本身引入的記憶體洩漏會造成 native memory 的記憶體,嚴重情況下會造成 native memory 的 out of memory。

Global Reference 引入的記憶體洩漏

JNI 程式設計還要同時遵循 JNI 的規範标準,JVM 附加了 JNI 程式設計特有的記憶體管理機制。

JNI 中的 Local Reference 隻在 native method 執行時存在,當 native method 執行完後自動失效。這種自動失效,使得對 Local Reference 的使用相對簡單,native method 執行完後,它們所引用的 Java 對象的 reference count 會相應減 1。不會造成 Java Heap 中 Java 對象的記憶體洩漏。

而 Global Reference 對 Java 對象的引用一直有效,是以它們引用的 Java 對象會一直存在 Java Heap 中。程式員在使用 Global Reference 時,需要仔細維護對 Global Reference 的使用。如果一定要使用 Global Reference,務必確定在不用的時候删除。就像在 C 語言中,調用 malloc() 動态配置設定一塊記憶體之後,調用 free() 釋放一樣。否則,Global Reference 引用的 Java 對象将永遠停留在 Java Heap 中,造成 Java Heap 的記憶體洩漏。

1、什麼需要釋放? 

什麼需要什麼呢 ? JNI 基本資料類型是不需要釋放的 , 如 jint , jlong , jchar 等等 。 我們需要釋放是引用資料類型,當然也包括數組家族。如:jstring ,jobject ,jobjectArray,jintArray 等等。

當然,大家可能經常忽略掉的是 jclass ,jmethodID , 這些也是需要釋放的哦

2、如何去釋放?

1)      釋放String

jstring jstr = NULL;

char* cstr = NULL;

//調用方法

jstr = (*jniEnv)->CallObjectMethod(jniEnv, mPerson, getName);

cstr = (char*) (*jniEnv)->GetStringUTFChars(jniEnv,jstr, 0);

__android_log_print(ANDROID_LOG_INFO, "JNIMsg", "getName  ---->   %s",cstr );

//釋放資源

(*jniEnv)->ReleaseStringUTFChars(jniEnv, jstr, cstr);

(*jniEnv)->DeleteLocalRef(jniEnv, jstr);

2)      釋放 類 、對象、方法

(*jniEnv)->DeleteLocalRef(jniEnv, XXX);

“XXX” 代表 引用對象

3)      釋放 數組家族

jobjectArray arrays = NULL;

jclass jclsStr = NULL;

jclsStr = (*jniEnv)->FindClass(jniEnv, "java/lang/String");

arrays = (*jniEnv)->NewObjectArray(jniEnv, len, jclsStr, 0);

(*jniEnv)->DeleteLocalRef(jniEnv, jclsStr);  //釋放String類

(*jniEnv)->DeleteLocalRef(jniEnv, arrays); //釋放jobjectArray數組

native method 調用 DeleteLocalRef() 釋放某個 JNI Local Reference 時,首先通過指針 p 定位相應的 Local Reference 在 Local Ref 表中的位置,然後從 Local Ref 表中删除該 Local Reference,也就取消了對相應 Java 對象的引用(Ref count 減 1)。

下面舉一些具體的例子:

1.FindClass 

例如,

jclass ref= (env)->FindClass("java/lang/String");

env->DeleteLocalRef(ref); 
           

2.NewString/ NewStringUTF/NewObject/NewByteArray

例如,

jstring     (*NewString)(JNIEnv*, const jchar*, jsize);   

const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);

jstring     (*NewStringUTF)(JNIEnv*, const char*);   

const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
           

使用env->DeleteLocalRef(ref); 釋放資源。

3.GetObjectField/GetObjectClass/GetObjectArrayElement

jclass ref = env->GetObjectClass(robj);

env->DeleteLocalRef(ref);
           

4.GetByteArrayElements和GetStringUTFChars

jbyte* array= (*env)->GetByteArrayElements(env,jarray,&isCopy);
(*env)->ReleaseByteArrayElements(env,jarray,array,0);

const char* input =(*env)->GetStringUTFChars(env,jinput, &isCopy);
(*env)->ReleaseStringUTFChars(env,jinput,input);
           

5.NewGlobalRef/DeleteGlobalRef

jobject ref= env->NewGlobalRef(customObj);
env->DeleteGlobalRef(customObj);
           

這裡可能有一些需要注意的,如果建立的數組,結構體作為傳回值給android,android可能會自動釋放,不需要jni再進行釋放。這個大家可以去查找資料看看,我對這些也不很清楚。

android jni 釋放資源就講完了。

就這麼簡單。