Efficient JNI programming II: Field and method access

In my last installment I discussed some topics that help you decide if you need to implement a native method at all, and if yes, then what there is to consider. This time I want to talk about one of the most common pitfalls in JNI programming, field and method access.

Suppose you need to access a field in a Java object. The straightforward naive approach to this would look like:

JNIEXPORT void JNICALL Java_JNIStuff_myNativeMethod(JNIEnv* env, jobject this) {
  jclass objClass = (*env)->FindClass(env, obj);
  jfieldID myFieldID = (*env)->GetFieldID(env, objClass, "myField", "I");
  jint fieldVal = (*env)->GetIntField(env, obj, myFieldID);
  printf("myField has value: %d\n", fieldVal);
}

This is not only overly verbose for something simple as getting a field (other languages do this like obj->myField), it also has a glaring problem: The GetFieldID() function is quite a performance hole. In the worst case it has to process the strings, and lookup the fieldID in the corresponding class data. And even in the normal case (when the fieldID might already be stored somewhere) it needs to process the strings and do some hash lookup.

So the general advice you often hear is to not do field access (and method access alike) at all in JNI. You might ask, how is that possible to do? You could simply avoid dealing with objects in JNI at all, and pass all the primitive data you need to the native method, and do the field access on the Java side. That’s actually not the worst idea, but there’s also other approaches which I’d like to discuss.

When GetFieldID() is so slow, it probably makes you wonder why it has been included in JNI at all. A native interface which is inherently slow doesn’t make much sense, does it? Well, the answer is, GetFieldID() simply should never be used like shown above. The idea is that native code should always cache its field IDs and method IDs. A common approach for this would look like this:

jfieldID myFieldID = NULL;

JNIEXPORT void JNICALL Java_JNIStuff_myNativeMethod(JNIEnv* env, jobject this) {
  jint fieldVal;
  jclass objClass;

  if (myFieldID == NULL) {
    objClass = (*env)->FindClass(env, obj);
    myFieldID = (*env)->GetFieldID(env, objClass, "myField", "I");
  }

  fieldVal = (*env)->GetIntField(env, obj, myFieldID);
  printf("myField has value: %d\n", fieldVal);

}

However, this approach has two flaws. First and less important, it has to perform the if-check on each execution. This is probably a minor performance hit compared to GetFieldID(), but still. The other more important problem is the lifetime of the field ID. According to the specification, the field ID is guaranteed to be valid only as long as the class is loaded. So, if for some reason the class gets unloaded, and then loaded again, a former field ID is probably invalid. You can work around that by storing the jclass reference as a global reference somewhere, thus preventing the GC from unloading the class.

The clean and correct approach makes use of the lifetime of the field (or method) ID. The idea is to always cache the IDs when the class gets loaded. In order to do this, we add a native method initIDs(), which fetches the IDs that we need like this:

jfieldID myFieldID;

JNIEXPORT void JNICALL Java_JNIStuff_initIDs(JNIEnv* env, jclass cls) {
  myFieldID = (*env)->GetFieldID(env, cls, "myField", "I");
}

JNIEXPORT void JNICALL Java_JNIStuff_myNativeMethod(JNIEnv* env, jobject this) {
  jint fieldVal = (*env)->GetIntField(env, obj, myFieldID);
  printf("myField has value: %d\n", fieldVal);
}

Looks better, eh? The good solution is always the nicest looking 😉 Now, when the class gets unloaded and re-loaded, the IDs get initialized again, and you wouldn’t shoot yourself in the foot with class unloading.

However, sometimes it’s not that easy. What if you need to access a field that’s in another class, to which you don’t have access to the sourcecode to add a class initializer? I think, in such a case it’s time to re-think your design. In most such cases it’s possible to refactor things so that you only need to access fields or methods in your class, possibly by wrapping the ‘alien’ class somehow. If you need to access a non-visible field in that alien class, my answer is simply DON’T DO THIS.

Another important aspect of the last solution is, you always need to initialize the IDs for one class in the initializer of that class, and not in the initializer of another class. Otherwise you subvert the whole point of avoiding class unloading badness.

Summary: Initialize all your field IDs in the class initializer of the class to which the fields belong. This whole discussion applies in the very same way to method IDs.

In the next installation I’ll discuss various ways to transfer and access array data from Java to native and back.

Advertisements

6 Responses to Efficient JNI programming II: Field and method access

  1. Pingback: This note’s for you » Blog Archive » Efficient JNI programming, part I

  2. pkphilip says:

    Thanks for your blog entries on JNI. Very interesting.

    I am also interested in knowing what are the performance penalties if say, a java class has to be called / accessed from a C/C++ program.

    To give you a scenario – lets say I implement a Java class for configuration management, can I then access this class from a C/C++ program without incurring a huge performance overhead?

    Thanks,
    pkphilip

  3. roman says:

    I must admit that I never did that. I think, depending on how you implement that, the performance would be horrible or just perfect. The following points need to be considered:
    * A Java VM must be started. I’d do this at some global initialization point using the JNI invocation, so that the Java method access is reasonably fast.
    * The corresponding Java classes need to be loaded. This can take some time, especially when you need to do this on each call into Java land. Make sure you keep a global reference of your key Java classes around to prevent instant unloading.

    Other than that, I can see no real problems in doing that. If implemented with these considerations in mind (and those that I outline in my small series of postings) then a call to a Java method from a C/C++ program should be just as efficient as calling a Java method from Java.

  4. Amit says:

    Great post. I have a question: When do you call the native method initIDs that caches the fieldIDs? Is it in the static initializer of a class?

  5. roman says:

    Amit: Usually, yes. The reason for this is, that the field/methodIDs become invalid when a class is unloaded. So we initialize it when it’s loaded (again). However, there might be situations where this isn’t possible, then you have to find a different solution.

  6. Satyavathi says:

    Nice post. Would like to know about your next installation – various ways to transfer and access array data from Java to native and back.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: