Rules for Managing References

Một phần của tài liệu The java™ native interface (Trang 84 - 87)

You should call DeleteWeakGlobalRef when your native code no longer needs access to a weak global reference. If you fail to call this function the Java virtual machine will still be able to garbage collect the underlying object, but will not be able to reclaim the memory consumed by the weak global reference itself.

5.3 Rules for Managing References

We are now ready to go through the rules for managing JNI references in native code, based on what we have covered in previous sections. The objective is to eliminate unnecessary memory usage and object retention.

There are, in general, two kinds of native code: functions that directly imple- ment native methods and utility functions that are used in arbitrary contexts.

When writing functions that directly implement native methods, you need to be careful about excessive local reference creation in loops and unnecessary local reference creation caused by native methods that do not return. It is acceptable to leave up to 16 local references in use for the virtual machine to delete after the native method returns. Native method calls must not cause global or weak global references to accumulate because global and weak global references are not freed automatically after native methods return.

When writing native utility functions you must be careful not to leak any local references on any execution path throughout the function. Because a utility func- tion may be called repeatedly from an unanticipated context, any unnecessary ref- erence creation may cause memory overflow.

When a utility function that returns a primitive type is called, it must not have the side effect of accumulating additional local, global, or weak global refer- ences.

• When a utility function that returns a reference type is called, it must not accumulate extra local, global, or weak global references, other than the refer- ence it returns as result.

It is acceptable for a utility function to create some global or weak global ref- erences for the purpose of caching because only the very first call creates these references.

If a utility function returns a reference, you should make the kind of returned reference part of the function specification. It should not return a local reference some of the time and a global reference at other times. The caller needs to know the type of the reference returned by a utility function in order to manage its own JNI references correctly. For example, the following code calls a utility function

GetInfoString repeatedly. We need to know the type of reference returned by

LOCAL AND GLOBAL REFERENCES Rules for Managing References 5.3 GetInfoStringto be able to free the returned JNI reference properly after each

iteration.

while (JNI_TRUE) {

jstring infoString = GetInfoString(info);

... /* process infoString */

??? /* we need to call DeleteLocalRef, DeleteGlobalRef, or DeleteWeakGlobalRef depending on the type of reference returned by GetInfoString. */

}

In Java 2 SDK release 1.2, theNewLocalReffunction sometimes is useful to ensure that a utility function always returns a local reference. To illustrate, let us make another (somewhat contrived) change to theMyNewStringfunction. The fol- lowing version caches a frequently requested string (say,"CommonString") in a global reference:

jstring

MyNewString(JNIEnv *env, jchar *chars, jint len) {

static jstring result;

/* wstrncmp compares two Unicode strings */

if (wstrncmp("CommonString", chars, len) == 0) {

/* refers to the global ref caching "CommonString" */

static jstring cachedString = NULL;

if (cachedString == NULL) {

/* create cachedString for the first time */

jstring cachedStringLocal = ... ;

/* cache the result in a global reference */

cachedString =

(*env)->NewGlobalRef(env, cachedStringLocal);

}

return (*env)->NewLocalRef(env, cachedString);

}

... /* create the string as a local reference and store in result as a local reference */

return result;

}

The normal path returns a string as a local reference. As explained before, we must store the cached string in a global reference so that it can be accessed in mul- tiple native method invocations and from multiple threads. The highlighted line creates a new local reference that refers to the same object as the cached global

5.3 Rules for Managing References LOCAL AND GLOBAL REFERENCES

reference. As part of the contract with its callers,MyNewStringalways returns a local reference.

ThePush/PopLocalFramefunctions are especially convenient for managing the lifetime of local references. If you calledPushLocalFrameon entry to a native function, calling PopLocalFrame before the native function returns ensures that all local references created during native function execution will be freed. The

Push/PopLocalFramefunctions are efficient. You are strongly encouraged to use them.

If you call PushLocalFrame on function entry, remember to call Pop- LocalFrame in all function exit paths. For example, the following function has one call toPushLocalFrame but needs multiple calls toPopLocalFrame:

jobject f(JNIEnv *env, ...) {

jobject result;

if ((*env)->PushLocalFrame(env, 10) < 0) {

/* frame not pushed, no PopLocalFrame needed */

return NULL;

} ...

result = ...;

if (...) {

/* remember to pop local frame before return */

result = (*env)->PopLocalFrame(env, result);

return result;

} ...

result = (*env)->PopLocalFrame(env, result);

/* normal return */

return result;

}

Failing to place PopLocalFrame calls properly would lead to undefined behavior, such as virtual machine crashes.

The above example also illustrates why it is sometimes useful to specify the second argument to PopLocalFrame. Theresultlocal reference is initially cre- ated in the new frame constructed byPushLocalFrame.PopLocalFrameconverts its second argument,result, to a new local reference in the previous frame before popping the topmost frame.

C H A P T E R 6

Exceptions

WE have encountered numerous situations in which native code checks for possible errors after making JNI function calls. This chapter examines how native code can detect and recover from these error conditions.

We will focus on errors that occur as the result of issuing JNI function calls, not arbitrary errors that happen in native code. If a native method makes an oper- ating systems call, it simply follows the documented way of checking for possible failures in the system call. If, on the other hand, the native method issues a call- back to a Java API method, then it must follow the steps described in this chapter to properly check for and recover from possible exceptions that have occurred in the method execution.

Một phần của tài liệu The java™ native interface (Trang 84 - 87)

Tải bản đầy đủ (PDF)

(318 trang)