2013-07-08 37 views
5

我的應用需要使用jni。邏輯是這樣的:如何正確釋放循環中的jstring?

void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) { 
    int count = 10; 
    struct MyObj *myObjArray = (struct MyObj*)malloc(sizeof(struct MyObj) * count); 
    for (i = 0; i < count; i++) { 
     jobject obj = (*env)->GetObjectArrayElement(env, items, i); 
     jfieldID fieldId = ...; 
     jstring jstr = (*env)->GetObjectField(env, obj, fieldId); 
     myObjArray[i].name = (*env)->GetStringUTFChars(env, jstr); 
     (*env)->DeleteLocalRef(env, obj); 
     // Location A 
    } 

    // some code which will use myObjArray 
    process(count, myObjectArray); 

    // Location B 
} 

並通過JNI的文檔,通過GetStringUTFChars陣列回報應該使用relased

(*env)->ReleaseStringUTFChars(env, jstr, myObjArray[i].name); 
(*env)->ReleaseLocalRef(env, jstr); 
  1. 如果我釋放在位置A返回數組,然後myObjArray.name會空
  2. 如果我釋放位置B返回的數組,因爲我會保留jstring的引用,那麼「失敗將添加到JNI本地ref表(有512個條目)」將會發生

我的問題是: 如果我想正確釋放jstring,該怎麼辦?

+0

MyObj的「name」字段實際上是否應該保存'jstring',還是應該保存C'char *'?看起來好像是後者,在這種情況下,您應該從每個字符串中獲取字符並將它們複製到新分配的char數組中,然後將這些字符存儲在data數據字段中,然後釋放Java字符。 –

+0

@Ernest Friedman-Hill對不起,我忘了添加一行調用「GetStringUTFChars」的行。我再次編輯問題。謝謝。 – cmoaciopm

回答

6

由於您的循環正在創建本地引用(GetObjectField),因此您需要在循環中釋放它(DeleteLocalRef),否則您將在本地引用的限制內運行。您必須完整處理兩個調用之間的Java字符串。

由於您希望保留要在循環外部使用的字符串的字節,因此需要複製字節,因爲JVM的固定(或臨時副本)(GetStringUTFChars)必須在字符串引用之前釋放(ReleaseStringUTFChars)發行了。

所以對於循環內的字符串的順序必須是:

  1. GetObjectField
  2. GetStringUTFChars
  3. 使自己的副本
  4. ReleaseStringUTFChars
  5. DeleteLocalRef

注意:通過GetStringUTFChars,您將獲得指向修改後的Java String的UTF-8編碼的指針。這裏有兩點:

  1. 你的代碼應該能夠處理修改過的UTF-8編碼字符。 (每個字符有1到6個字節,並以特殊的方式對NUL進行編碼。)
  2. 文檔不會說數組是否以0結尾。您可以使用GetStringUTFLength獲取修改的UTF-8編碼中的字節數 - 不計算任何0終止符。 (各種JNI實現和The Book確實同意數組是0終止的。)如果你想用自己的終結器製作你自己的副本,一定要爲終結者增加空間。

如果您寧願使用UTF-16編碼,請使用GetStringCharsGetStringLength。在這種情況下,數組絕對不會被終止;它使用內部計數和字符串字節而不進行任何轉換。或者,如果您想更改字符集,比如實際的「UTF-8」,「ASCII」,「CP437」或「Windows-1252」或您的代碼可以處理的其他東西,請使用String.getBytes過載或Charset類。如果要控制如何處理目標字符集中不支持的字符,請使用Charset類。

+0

MUTF-8字符串是空終止的。 – fadden

+0

@fadden謝謝。爲了保持實用性,我改變了措辭。 –

0

經過一些實驗後,我得到了答案,但我不確定它是否完全正確。 我刪除了循環中的jstring引用,錯誤「無法添加到JNI本地引用表(有512個條目)」不再發生。

void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) { 
    int count = 10; 
    jstring tempArray[count]; 
    struct MyObj *myObjArray = (struct MyObj*)malloc(sizeof(struct MyObj) * count); 
    for (i = 0; i < count; i++) { 
     jobject obj = (*env)->GetObjectArrayElement(env, items, i); 
     jfieldID fieldId = ...; 
     jstring jstr = (*env)->GetObjectField(env, obj, fieldId); 
     myObjArray[i].name = (*env)->GetStringUTFChars(env, jstr); 
     (*env)->DeleteLocalRef(env, obj); 

     // Location A 
     tempArray[i] = jstr; 
     (*env)->DeleteLocalRef(jstr); 
    } 

    // some code which will use myObjArray 
    process(count, myObjectArray); 

    // Location B 
    for (i = 0; i < count; i++) { 
     (*env)->ReleaseStringUTFChars(env, tempArray[i], myObjectArray[i].name); 
    } 
} 

我擔心的是: 的功能「ReleaseStringUTFChars」的第二個參數必須是一個的jstring。 因此,我創建一個數組來保存jstring的引用以供以後發佈。 當我刪除循環中的jstring的引用,這意味着jstring被釋放。 有沒有什麼問題我在這裏通過那個jstring調用「ReleaseStringUTFChars」? 通過我的測試,我沒有遇到任何問題。

+1

這可能是所謂的「未定義行爲」。對字符串的本地引用已經被釋放,但字符串仍然被釘住(或者已經爲拷貝分配了內存),然後使用本地引用執行另一個操作。 –

+2

刪除後請勿使用本地參考。我希望CheckJNI模式拒絕電話。當前實現在Release調用中不使用jstring引用,但可能會改變。 – fadden