JNI, освобождение примитивных массивов и исключения из памяти - PullRequest
1 голос
/ 08 февраля 2011

Допустим, у меня есть функция со следующим прототипом:

JNIEXPORT void JNICALL Java_example_SCLASS_cfunc
(JNIEnv *env, jclass caller, jdoubleArray s, jdoubleArray u, jdoubleArray vt)

Я хочу сделать что-то вроде этого:

{
  jdouble* S_native = (*env)->GetDoubleArrayElements(env, s, JNI_FALSE); 
  jdouble* U_native = (*env)->GetDoubleArrayElements(env, u, JNI_FALSE);
  jdouble* VT_native = (*env)->GetDoubleArrayElements(env, vt, JNI_FALSE);  

  if(!S_native || !U_native || !VT_native){
    (*env)->ReleaseDoubleArrayElements(env, s, S_native, 0);
    (*env)->ReleaseDoubleArrayElements(env, u, U_native, 0);
    (*env)->ReleaseDoubleArrayElements(env, vt, VT_native, 0); 
    return; 
  }

  /*Now Use the arrays in some way...*/

  (*env)->ReleaseDoubleArrayElements(env, s, S_native, 0);
  (*env)->ReleaseDoubleArrayElements(env, u, U_native, 0);
  (*env)->ReleaseDoubleArrayElements(env, vt, VT_native, 0); 
  return;
}

Но я не уверен, что смогу это сделать, потому что я прочитал в документации jni, что Вы должны вернуться, как только сгенерировано Java-исключение, т.е. (* env) -> GetDoubleArray ... завершается неудачей.

Так что я не уверен, что произойдет, если вы сделаете еще один вызов GetDoubleArray после того, как предыдущий сбой.

Так что, несмотря на неопределенность, мой код отформатирован следующим образом:

{
  jdouble* S_native;
  jdouble* U_native;
  jdouble* VT_native;

  S_native = (*env)->GetDoubleArrayElements(env, s, JNI_FALSE); 
  if(!S_native){
    return;
  }

  U_native = (*env)->GetDoubleArrayElements(env, u, JNI_FALSE); 
  if(!U_native){
    (*env)->ReleaseDoubleArrayElements(env, s, S_native, 0);
    return;
  }

  VT_native = (*env)->GetDoubleArrayElements(env, vt, JNI_FALSE);
  if(!VT_native){
    (*env)->ReleaseDoubleArrayElements(env, s, S_native, 0);
    (*env)->ReleaseDoubleArrayElements(env, u, U_native, 0);
    return;
  } 

  /*Now Use the arrays in some way...*/

  (*env)->ReleaseDoubleArrayElements(env, s, S_native, 0);
  (*env)->ReleaseDoubleArrayElements(env, u, U_native, 0);
  (*env)->ReleaseDoubleArrayElements(env, vt, VT_native, 0); 
  return;
}

Это необходимо или я могу сделать это первым способом?

1 Ответ

3 голосов
/ 21 июня 2011

Прежде всего, внимательно прочитайте документацию по функции Get<TYPE>ArrayElements. Третий аргумент - указатель, через который вы получаете дополнительную информацию из возвращаемой функции (это копия собственного массива). Ваше решение работает только потому, что JNI_FALSE эквивалентно NULL.

Так что, если вам действительно нужно, вы должны написать:

jboolean isCopy;
jdouble* nativeArr = (*env)->GetDoubleArrayElements(env, arr, &isCopy);

Тогда вы знаете, работаете ли вы с скопированным массивом или с закрепленным. БОЛЬШЕ случаев, хотя вам не нужна эта информация. Вы просто выполняете обе функции самым простым способом:

jdouble* nativeArr = (*env)->GetDoubleArrayElements(env, arr, NULL);
// check nativeArr != NULL
// operate on array
(*env)->ReleaseDoubleArrayElements(env, arr, nariveArr, 0);

Рассматривая освобождение ресурса и проверку ошибок: если мы получим NULL от Get<>ArrayElements, это означает, что мы OutOfMemory и облажались. Приложение не может быть запущено после этой ошибки, поэтому я не буду беспокоиться о уже выделенных ресурсах. Оставьте это для очистки системы.

Но для того, чтобы это решение работало должным образом , вам нужно поднять OutOfMemoryError из своего кода!

Я использую макросы в своих проектах для очистки кода:

#define D_ARR_GET(narray, array) if((narray = (*env)->GetDoubleArrayElements(env, (array), NULL)) == NULL) { sendOutOfMemory(env); return; }
#define D_ARR_FREE(narray, array) ((*env)->ReleaseDoubleArrayElements(env, (array), (narray), 0))

void sendOutOfMemory(JNIEnv* env) {
     jclass oomCls = (*env)->FindClass(env, "java_lang_OutOfMemoryError");   
     jmethodID errInit = (*env)->GetMethodID(env, oomCls, "<init>", "void(V)");
     jobject exc = (*env)->NewObject(env, oomCls, errInit);

    (*env)->ExceptionClear(env);
    (*env)->Throw(env, (jthrowable) exc);
}

void someJNIfunc(JNIEnv* env, jobject arr) {
    jdouble* nativeArr; D_ARR_GET(nativeArr, arr);
    // use nativeArr
    D_ARR_FREE(nativeArr, arr);
}

Помните, что env->Throw не подразумевает возвращение обратно в Java, поэтому вам нужно return самостоятельно, распространяясь через собственный стек фреймов при необходимости (возможно, используя C ++ с исключениями, чтобы сделать его менее утомительным в более сложных решениях) .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...