JNI,释放原始数组和内存不足异常

时间:2011-02-08 17:52:35

标签: java java-native-interface

假设我有以下原型的功能:

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 个答案:

答案 0 :(得分:3)

首先仔细阅读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);

考虑释放资源和错误检查:如果我们从Get<>ArrayElements收到NULL,这意味着我们 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 ++,使其不再繁琐)更复杂的解决方案)。

相关问题