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).
精彩评论