7

私のアプリは jni を使用する必要があります。ロジックは次のようになります。

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 doc を介して、GetStringUTFChars によって返される配列は、次を使用して解放する必要があります。

(*env)->ReleaseStringUTFChars(env, jstr, myObjArray[i].name);
(*env)->ReleaseLocalRef(env, jstr);
  1. 返された配列を場所 A で解放すると、myObjArray.name は空になります
  2. 返された配列を場所 B で解放すると、jstring の参照が保持されるため、「JNI ローカル ref テーブルへの追加に失敗しました (512 エントリがあります)」が発生します。

私の質問は次のとおりです: jstring を正しく解放したい場合、どうすればよいですか?

4

3 に答える 3

7

ループはローカル参照 (GetObjectField) を作成しているため、ループ内でそれを解放 (DeleteLocalRef) する必要があります。そうしないと、ローカル参照の制限に達してしまいます。2 回の呼び出しの間に Java 文字列を完全に処理する必要があります。

ループの外で使用するために文字列のバイトを保持したいので、バイトをコピーする必要があります。これは、文字列参照が解放される前に、JVM の固定 (または一時コピー) (GetStringUTFChars) を解放 (ReleaseStringUTFChars) する必要があるためです。

したがって、ループ内の文字列のシーケンスは次のようになります。

  1. GetObjectField
  2. GetStringUTFChars
  3. 独自のコピーを作成する
  4. ReleaseStringUTFChars
  5. DeleteLocalRef

注: GetStringUTFCharsJava 文字列の変更された UTF-8 エンコーディングへのポインターを取得しています。ここで 2 つのポイント:

  1. コードは、変更された UTF-8 でエンコードされた文字を処理できる必要があります。(1 文字あたり 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ターゲット文字セットでサポートされていない文字の処理方法を制御する場合は、このクラスを使用します。

于 2013-07-08T14:42:28.470 に答える
0

いくつかの実験の後、答えが得られますが、それが正確に正しいかどうかはわかりません。ループ内の jstring の参照を削除すると、「JNI ローカル ref テーブルへの追加に失敗しました (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」の2番目のパラメーターはjstringである必要があります。そこで、後のリリース用に jstring の参照を保持する配列を作成します。ループ内の jstring の参照を削除すると、jstring が解放されます。ここでそのjstringに対して「ReleaseStringUTFChars」と呼ぶ問題はありますか? 私のテストでは、問題は発生していません。

于 2013-07-08T07:07:41.850 に答える