开发者

JNI, freeing primitive arrays, and out of memory exceptions

Lets say I have a function with the following prototype:

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

I want to do something like this:

{
  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;
}

But I'm not sure if I can do that because I read in the jni documentation that you should return as soon as a java exception is generated, i.e (*env)->GetDoubleArray... fails.

So I'm uncertain what happens if you make another GetDoubleArray call after a previous has failed.

So in the face of uncertainty I annoyingly have my code formatted like so:

{
  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 = (*e开发者_Go百科nv)->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;
}

Is this necessary or can I do it the first way?


First of all read carefully documentation for Get<TYPE>ArrayElements function. The third argument to it is a pointer through which you got additional information from function returned (is the native array a copy). Your solution works only because JNI_FALSE is equivalent to NULL.

So if you really need to you should've written:

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

Then you know if you are operating on copied array or pinned one. MOST of the times though you don't need that information. You just both functions in a simplest way:

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

Considering freeing resource and error checking: if we receive NULL from Get<>ArrayElements that means we are OutOfMemory and fucked up. Application cannot run after this error, so I wouldn't care about already allocated resources. Leave it for the system to clean up.

But for this solution to work as it should you need to raise OutOfMemoryError from your code!

I am using macros in my projects to clean up the code:

#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);
}

Remember than env->Throw doesn't imply returning back to Java, so you need to return yourself, propagating through native frame stack if needed (maybe using C++ with exceptions to make it less tedious in a more complicated solutions).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜