tags:

views:

352

answers:

3

This is a question related to a previous post, but this post was solved and now I wanted to change the direction of the question.

When working with JNI, it is necessary to ask the JNIEnv object for jclass and jmethodID for each class and method that will be used in the C/C++ code. Just to be clear, I want to call Java contructors or methods from C/C++.

Since the communication from Java to C/C++ (and viceversa) is costly, I initially thought that one way to minimize this was to reuse the jclass and jmethodID. Therefore, I saved this instances in global variables as follows:

jclass    someClass  = NULL;
jmethodID someMethod = NULL;

JNIEXPORT jobject JNICALL Java_example_method1(JNIEnv *env, jobject jobj) {
    // initialize someClass and someMethod if they are NULL
    // use someClass and someMethod to call java (for example, thru NewObject)
}

JNIEXPORT jobject JNICALL Java_example_method2(JNIEnv *env, jobject jobj) {
    // initialize someClass and someMethod if they are NULL
    // use someClass and someMethod to call java again
}

A more specific (and useful) example, which I use to throw exceptions from anywhere in my JNI functions:

jclass    jniExceptionClass           = NULL;

void throwJavaException(JNIEnv *env, const char* msg) {
    if (!jniExceptionClass) {
        jniExceptionClass = env->FindClass("example/JNIRuntimeException");
    }
    if (jniExceptionClass)
        env->ThrowNew(jniExceptionClass, msg);
    }
}

The problem is that I continued to use this pattern and got a segmentation fault that was only solved by not-reusing this variables (this was the solution to the previous post).

The questions are:

  • Why is it illegal to reuse the jclass and jmethodID thru different JNI functions? I thought that this values were always the same.
  • Just for curiosity: what is the impact/overhead of initializing all necessary jclass and jmethodID for each JNI function?
+1  A: 

As I remember jclass is will be local to the calling method so can't be cached, however the method id can be. See http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/design.html for more detaild info.

Sorry, I don't know about the performance aspect, any time I've used JNI it has been insignificant compared to the task in hand.

vickirk
+5  A: 

The rules here are clear. Method ID and field ID values are forever. You can hang onto them. The lookups take some time.

jclass, on the other hand, is generally a local reference. This survive, at most, the duration of a single call to a JNI function.

If you need to optimize, you have to ask the JVM to make a global reference for you. It's not uncommon to acquire and keep references to common classes like java.lang.String.

Holding such a reference to a class will prevent it (the class) from being garbage-collected, of course.

jclass local = env->FindClass(CLS_JAVA_LANG_STRING);
_CHECK_JAVA_EXCEPTION(env);
java_lang_string_class = (jclass)env->NewGlobalRef(local);
_CHECK_JAVA_EXCEPTION(env);
env->DeleteLocalRef(local);
_CHECK_JAVA_EXCEPTION(env);
bmargulies
JNI automatically cleans up the local references returned by `FindClass` when going back to the Java side.
Gregory Pakosz
thus, 'at most'.
bmargulies
in this link ( http://java.sun.com/docs/books/jni/html/other.html ), they use NewWeakGlobalRef. I will try with the weak reference.
YuppieNetworking
+1  A: 

Inside JNI_OnLoad, you need to use NewGlobalRef on the jclass values returned by FindClass before caching them.

Then, inside JNI_OnUnload you call DeleteGlobalRef on them.

Gregory Pakosz