| Contents | Prev | Next | Index | The Java Native Interface Programmer's Guide and Specification |
One of the most common questions programmers ask when interfacing Java applications with native code is how data types in the Java programming language map to the data types in native programming languages such as C and C++. In the "Hello World!" example presented in the last chapter, we did not pass any arguments to the native method, nor did the native method return any result. The native method simply printed a message and returned.
In practice, most programs will need to pass arguments to native methods, and receive results from native methods as well. In this chapter, we will describe how to exchange data types between code written in the Java programming language and the native code that implements native methods. We will start with primitive types such as integers and common object types such as strings and arrays. We will defer the full treatment of arbitrary objects to the next chapter, where we will explain how the native code can access fields and make method calls.
Let us start with a simple example that is not too different from the HelloWorld program in the last chapter. The example program, Prompt.java, contains a native method that prints a string, waits for user input, and then returns the line that the user has typed in. The source code for this program is as follows:
class Prompt {
// native method that prints a prompt and reads a line
private native String getLine(String prompt);
public static void main(String args[]) {
Prompt p = new Prompt();
String input = p.getLine("Type a line: ");
System.out.println("User typed: " + input);
}
static {
System.loadLibrary("Prompt");
}
}
Prompt.main calls the native method Prompt.getLine to receive user input. The static initializer calls the System.loadLibrary method to load a native library called Prompt.
The Prompt.getLine method can be implemented with the following C function:
JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt);
You can use the javah tool (§2.4) to generate a header file containing the above function prototype. The JNIEXPORT and JNICALL macros (defined in the jni.h header file) ensure that this function is exported from the native library and C compilers generate code with the correct calling convention for this function. The name of the C function is formed by concatenating the "Java_" prefix, the class name, and the method name. Section 11.3 contains a more precise description of how the C function names are formed.
As briefly discussed in Section 2.4, the native method implementation such as Java_Prompt_getLine accepts two standard parameters, in addition to the arguments declared in the native method. The first parameter, the JNIEnv interface pointer, points to a location that contains a pointer to a function table. Each entry in the function table points to a JNI function. Native methods always access data structures in the Java virtual machine through one of the JNI functions. Figure 3.1 illustrates the JNIEnv interface pointer.
JNIEnv Interface Pointer
The second argument differs depending on whether the native method is a static or an instance method. The second argument to an instance native method is a reference to the object on which the method is invoked, similar to the this pointer in C++. The second argument to a static native method is a reference to the class in which the method is defined. Our example, Java_Prompt_getLine, implements an instance native method. Thus the jobject parameter is a reference to the object itself.
Argument types in the native method declaration have corresponding types in native programming languages. The JNI defines a set of C and C++ types that correspond to types in the Java programming language.
There are two kinds of types in the Java programming language: primitive types such as int, float, and char, and reference types such as classes, instances, and arrays. In the Java programming language, strings are instances of the java.lang.String class.
The JNI treats primitive types and reference types differently. The mapping of primitive types is straightforward. For example, the type int in the Java programming language maps to the C/C++ type jint (defined in jni.h as a signed 32-bit integer), while the type float in the Java programming language maps to the C and C++ type jfloat (defined in jni.h as a 32-bit floating point number). Section 12.1.1 contains the definition of all primitive types defined in the JNI.
The JNI passes objects to native methods as opaque references. Opaque references are C pointer types that refer to internal data structures in the Java virtual machine. The exact layout of the internal data structures, however, is hidden from the programmer. The native code must manipulate the underlying objects via the appropriate JNI functions, which are available through the JNIEnv interface pointer. For example, the corresponding JNI type for java.lang.String is jstring. The exact value of a jstring reference is irrelevant to the native code. The native code calls JNI functions such as GetStringUTFChars (§3.2.1) to access the contents of a string.
All JNI references have type jobject. For convenience and enhanced type safety, the JNI defines a set of reference types that are conceptually "subtypes" of jobject. (A is a subtype of B of every instance of A is also an instance of B.) These subtypes correspond to frequently used reference types in the Java programming language. For example, jstring denotes strings; jobjectArray denotes an array of objects. Section 12.1.2 contains a complete listing of the JNI reference types and their subtyping relationships.
The Java_Prompt_getLine function receives the prompt argument as a jstring type. The jstring type represents strings in the Java virtual machine, and is different from the regular C string type (a pointer to characters, char *). You cannot use a jstring as a normal C string. The following code, if run, would not produce the desired results. In fact, it will most likely crash the Java virtual machine.
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
/* ERROR: incorrect use of jstring as a char* pointer */
printf("%s", prompt);
...
}
Your native method code must use the appropriate JNI functions to convert jstring objects to C/C++ strings. The JNI supports conversion both to and from Unicode and UTF-8 strings. Unicode strings represent characters as 16-bit values, whereas UTF-8 strings (§12.3.1) use an encoding scheme that is upward compatible with 7-bit ASCII strings. UTF-8 strings act like NULL-terminated C strings, even if they contain non-ASCII characters. All 7-bit ASCII characters whose values are between 1 and 127 remain the same in the UTF-8 encoding. A byte with the highest bit set signals the beginning of a multi-byte encoded 16-bit Unicode value.
The Java_Prompt_getLine function calls the JNI function GetStringUTFChars to read the contents of the string. The GetStringUTFChars function is available through the JNIEnv interface pointer. It converts the jstring reference, typically represented by the Java virtual machine implementation as a Unicode sequence, into a C string represented in the UTF-8 format. If you are certain that the original string contains only 7-bit ASCII characters, you may pass the converted string to regular C library functions such as printf. (We will discuss how to handle non-ASCII strings in Section 8.2.)
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const jbyte *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%s", buf);
return (*env)->NewStringUTF(env, buf);
}
Do not forget to check the return value of GetStringUTFChars. Because the Java virtual machine implementation needs to allocate memory to hold the UTF-8 string, there is a chance that memory allocation will fail. When that happens, GetStringUTFChars returns NULL and throws an OutOfMemoryError exception. As we will learn in Chapter 6, throwing an exception through the JNI is different from throwing an exception in the Java programming language. A pending exception thrown through the JNI does not automatically change control flow in native C code. Instead, we need to issue an explicit return statement in order to skip the remaining statements in the C function. After Java_Prompt_getLine returns, the exception will be thrown in Prompt.main, caller of the Prompt.getLine native method.
When your native code finishes using the UTF-8 string obtained through GetStringUTFChars, it calls ReleaseStringUTFChars. Calling ReleaseString-UTFChars indicates that the native method no longer needs the UTF-8 string returned by GetStringUTFChars; thus the memory taken by the UTF-8 string can be freed. Failure to call ReleaseStringUTFChars would result in a memory leak, which could ultimately lead to memory exhaustion.
You can construct a new java.lang.String instance in the native method by calling the JNI function NewStringUTF. The NewStringUTF function takes a C string with the UTF-8 format and constructs a java.lang.String instance. The newly constructed java.lang.String instance represents the same sequence of Unicode characters as the given UTF-8 C string.
If the virtual machine cannot allocate the memory needed to construct the java.lang.String instance, NewStringUTF throws an OutOfMemoryError exception and returns NULL. In this example, we do not need to check its return value because the native method returns immediately afterwards. If NewString-UTF fails, the OutOfMemoryError exception will be thrown in the Prompt.main method that issued the native method call. If NewStringUTF succeeds, it returns a JNI reference to the newly constructed java.lang.String instance. The new instance is returned by Prompt.getLine and then assigned to the local variable input in Prompt.main.
The JNI supports a number of other string-related functions, in addition to the GetStringUTFChars, ReleaseStringUTFChars, and NewStringUTF functions introduced earlier.
GetStringChars and ReleaseStringChars obtain string characters represented in the Unicode format. These functions are useful when, for example, the operating system supports Unicode as the native string format.
UTF-8 strings are always terminated with the `\0' character, whereas Unicode strings are not. To find out the number of Unicode characters in a jstring reference, JNI programmers can call GetStringLength. To find out how many bytes are needed to represent a jstring in the UTF-8 format, JNI programmers can either call the ANSI C function strlen on the result of GetStringUTFChars, or call the JNI function GetStringUTFLength on the jstring reference directly.
The third argument to GetStringChars and GetStringUTFChars requires additional explanation:
const jchar * GetStringChars(JNIEnv *env, jstring str, jboolean *isCopy);
Upon returning from GetStringChars, the memory location pointed to by isCopy will be set to JNI_TRUE if the returned string is a copy of the characters in the original java.lang.String instance. The memory location pointed to by isCopy will be set to JNI_FALSE if the returned string is a direct pointer to the characters in the original java.lang.String instance. When the location pointed to by isCopy is set to JNI_FALSE, native code must not modify the contents of the returned string. Violating this rule will cause the original java.lang.String instance to be modified as well. This breaks the invariant that java.lang.String instances are immutable.
Most often you pass NULL as the isCopy argument because you do not care whether the Java virtual machine returns a copy of the characters in the java.lang.String instance or a direct pointer to the original.
It is in general not possible to predict whether the virtual machine will copy the characters in a given java.lang.String instance. Programmers must therefore assume functions such as GetStringChars may take time and space proportional to the number of characters in the java.lang.String instance. In a typical Java virtual machine implementation, the garbage collector relocates objects in the heap. Once a direct pointer to a java.lang.String instance is passed back to the native code, the garbage collector can no longer relocate the java.lang.String instance. To put it another way, the virtual machine must pin the java.lang.String instance. Because excessive pinning leads to memory fragmentation, the virtual machine implementation may, at its discretion, decide to either copy the characters or pin the instance for each individual GetStringChars call.
Do not forget to call ReleaseStringChars when you no longer need access to the string elements returned from GetStringChars. The ReleaseString-Chars call is necessary whether GetStringChars has set *isCopy to JNI_TRUE or JNI_FALSE. ReleaseStringChars either frees the copy or unpins the instance, depending upon whether GetStringChars has returned a copy or not.
To increase the possibility that the virtual machine is able to return a direct pointer to the characters in a java.lang.String instance, Java 2 SDK release 1.2 introduces a new pair of functions, Get/ReleaseStringCritical. On the surface, they appear to be similar to Get/ReleaseStringChars functions in that both return a pointer to the characters if possible; otherwise, a copy is made. There are, however, significant restrictions on how these functions can be used.
You must treat the code inside this pair of functions as running in a "critical region." Inside a critical region, native code must not call arbitrary JNI functions, or any native function that may cause the current thread to block and wait for another thread running in the Java virtual machine. For example, the current thread must not wait for input on an I/O stream being written to by another thread.
These restrictions make it possible for the virtual machine to disable garbage collection when the native code is holding a direct pointer to string elements obtained via GetStringCritical. When garbage collection is disabled, any other threads that trigger garbage collection will be blocked as well. Native code between a Get/ReleaseStringCritical pair must not issue blocking calls or allocate new objects in the Java virtual machine. Otherwise, the virtual machine may deadlock. Consider the following scenario:
It is safe to overlap multiple pairs of GetStringCritical and Release-StringCritical functions. For example:
jchar *s1, *s2;
s1 = (*env)->GetStringCritical(env, jstr1);
if (s1 == NULL) {
... /* error handling */
}
s2 = (*env)->GetStringCritical(env, jstr2);
if (s2 == NULL) {
(*env)->ReleaseStringCritical(env, jstr1, s1);
... /* error handling */
}
... /* use s1 and s2 */
(*env)->ReleaseStringCritical(env, jstr1, s1);
(*env)->ReleaseStringCritical(env, jstr2, s2);
The Get/ReleaseStringCritical pairs need not be strictly nested in a stack order. We must not forget to check its return value against NULL for possible out of memory situations, because GetStringCritical might still allocate a buffer and make a copy of the array if the VM internally represents arrays in a different format. For example, the Java virtual machine may not store arrays contiguously. In that case, GetStringCritical must copy all the characters in the jstring instance in order to return a contiguous array of characters to the native code.
To avoid deadlocks, you must make sure that the native code does not call arbitrary JNI functions after it issues a GetStringCritical call and before it makes the corresponding ReleaseStringCritical call. The only JNI functions allowed in the "critical region" are overlapped Get/ReleaseStringCritical and Get/ReleasePrimitiveArrayCritical (§3.3.2) calls.
The JNI does not support GetStringUTFCritical and ReleaseStringUTF-Critical functions. Such functions would likely require the virtual machine to make a copy of the string, because virtual machines implementation almost certainly represent strings internally in the Unicode format.
Other additions to Java 2 SDK release 1.2 are GetStringRegion and GetStringUTF-Region. These functions copy the string elements into a preallocated buffer. The Prompt.getLine method may be reimplemented using GetStringUTFRegion as follows:
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
/* assume the prompt string and user input has less than 128
characters */
char outbuf[128], inbuf[128];
int len = (*env)->GetStringLength(env, prompt);
(*env)->GetStringUTFRegion(env, prompt, 0, len, outbuf);
printf("%s", outbuf);
scanf("%s", inbuf);
return (*env)->NewStringUTF(env, inbuf);
}
The GetStringUTFRegion function takes a starting index and length, both counted as number of Unicode characters. The function also performs bounds checking, and raises StringIndexOutOfBoundsException if necessary. In the above code, we obtained the length from the string reference itself, and are thus certain that there will be no index overflow. (The above code, however, lacks the necessary checks to ensure that the prompt string contains less than 128 characters.)
The code is somewhat simpler than using GetStringUTFChars. Because GetStringUTFRegion performs no memory allocation, we need not check for possible out-of-memory conditions. (Again, the above code lacks the necessary checks to ensure that the user input contains less than 128 characters.)
Table 3.1 summarizes all string-related JNI functions. Java 2 SDK 1.2 release adds a number of new functions that enhance performance for certain string operations. The added functions support no new operations other than bringing performance improvements.
Summary of JNI String Functions
Figure 3.2 illustrates how a programmer may choose among the string-related functions in JDK release 1.1 and Java 2 SDK release 1.2:
Figure 3.2 Choosing among the JNI String Functions
If you are targeting 1.1 or both 1.1 and 1.2 releases, there is no choice other than Get/ReleaseStringChars and Get/ReleaseStringUTFChars.
If you are programming in Java 2 SDK release 1.2 and above, and you want to copy the contents of a string into an already-allocated C buffer, use GetString-Region or GetStringUTFRegion.
For small fixed-size strings, Get/SetStringRegion and Get/SetString-UTFRegion are almost always the preferred functions because the C buffer can be allocated on the C stack very cheaply. The overhead of copying a small number of characters in the string is negligible.
One advantage of Get/SetStringRegion and Get/SetStringUTFRegion is that they do not perform memory allocation, and therefore never raise unexpected out-of-memory exceptions. No exception checking is necessary if you make sure that index overflow cannot occur.
Another advantage of Get/SetStringRegion and Get/SetStringUTF-Region is that the you can specify a starting index and the number of characters. These functions are suitable if the native code only needs to access a subset of characters in a long string.
GetStringCritical must be used with extreme care (§3.2.5). You must make sure that while holding a pointer obtained through GetStringCritical, the native code does not allocate new objects in the Java virtual machine or perform other blocking calls that may cause the system to deadlock.
Here is an example that demonstrates the subtle issues in the use of GetStringCritical. The following code obtains the content of a string and calls the fprintf function to write out the characters to the file handle fd:
/* This is not safe! */
const char *c_str = (*env)->GetStringCritical(env, j_str, 0);
if (c_str == NULL) {
... /* error handling */
}
fprintf(fd, "%s\n", c_str);
(*env)->ReleaseStringCritical(env, j_str, c_str);
The problem with the above code is that it is not always safe to write to a file handle when garbage collection is disabled by the current thread. Suppose, for example, that another thread T is waiting to read from the fd file handle. Let us further assume that the operating system buffering is set up in such a way that the fprintf call waits until the thread T finishes reading all pending data from fd. We have constructed a possible scenario for deadlocks: If thread T cannot allocate enough memory to serve as a buffer for reading from the file handle, it must request a garbage collection. The garbage collection request will be blocked until the current thread executes ReleaseStringCritical, which cannot happen until the fprintf call returns. The fprintf call is waiting, however, for thread T to finish reading from the file handle.
The following code, although similar to the example above, is almost certainly deadlock free:
/* This code segment is OK. */
const char *c_str = (*env)->GetStringCritical(env, j_str, 0);
if (c_str == NULL) {
... /* error handling */
}
DrawString(c_str);
(*env)->ReleaseStringCritical(env, j_str, c_str);
DrawString is a system call that directly writes the string onto the screen. Unless the screen display driver is also a Java application running in the same virtual machine, the DrawString function will not block indefinitely waiting for garbage collection to happen.
In summary, you need to consider all possible blocking behavior between a pair of Get/ReleaseStringCritical calls.
The JNI treats primitive arrays and object arrays differently. Primitive arrays contain elements that are of primitive types such as int and boolean. Object arrays contain elements that are of reference types such as class instances and other arrays. For example, in the following code segment written in the Java programming language:
int[] iarr; float[] farr; Object[] oarr; int[][] arr2;
iarr and farr are primitive arrays, whereas oarr and arr2 are object arrays.
Accessing primitive arrays in a native method requires the use of JNI functions similar to those used for accessing strings. Let us look at a simple example. The following program calls a native method sumArray that adds up the contents of an int array.
class IntArray {
private native int sumArray(int[] arr);
public static void main(String[] args) {
IntArray p = new IntArray();
int arr[] = new int[10];
for (int i = 0; i < 10; i++) {
arr[i] = i;
}
int sum = p.sumArray(arr);
System.out.println("sum = " + sum);
}
static {
System.loadLibrary("IntArray");
}
}
Arrays are represented by the jarray reference type and its "subtypes" such as jintArray. Just as jstring is not a C string type, neither is jarray a C array type. You cannot implement the Java_IntArray_sumArray native method by indirecting through a jarray reference. The following C code is illegal and would not produce the desired results:
/* This program is illegal! */
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
int i, sum = 0;
for (i = 0; i < 10; i++) {
sum += arr[i];
}
}
You must instead use the proper JNI functions to access primitive array elements, as shown in the following corrected example:
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint buf[10];
jint i, sum = 0;
(*env)->GetIntArrayRegion(env, arr, 0, 10, buf);
for (i = 0; i < 10; i++) {
sum += buf[i];
}
return sum;
}
The previous example uses the GetIntArrayRegion function to copy all the elements in the integer array into a C buffer (buf). The third argument is the starting index of the elements, and the fourth argument is the number of elements to be copied. Once the elements are in the C buffer, we can access them in native code. No exception checking is necessary because we know that 10 is the length of the array in our example, and thus there cannot be an index overflow.
The JNI supports a corresponding SetIntArrayRegion function that allows native code to modify the array elements of type int. Arrays of other primitive types (such as boolean, short, and float) are also supported.
The JNI supports a family of Get/Release<Type>ArrayElements functions (including, for example, Get/ReleaseIntArrayElements) that allow the native code to obtain a direct pointer to the elements of primitive arrays. Because the underlying garbage collector may not support pinning, the virtual machine may return a pointer to a copy of the original primitive array. We can rewrite the native method implementation in Section 3.3.1 using GetIntArrayElements as follows:
JNIEXPORT jint JNICALL
Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
{
jint *carr;
jint i, sum = 0;
carr = (*env)->GetIntArrayElements(env, arr, NULL);
if (carr == NULL) {
return 0; /* exception occurred */
}
for (i=0; i<10; i++) {
sum += carr[i];
}
(*env)->ReleaseIntArrayElements(env, arr, carr, 0);
return sum;
}
The GetArrayLength function returns the number of elements in primitive or object arrays. The fixed length of an array is determined when the array is first allocated.
Java 2 SDK release 1.2 introduces Get/ReleasePrimitiveArrayCritical functions. These functions allow virtual machines to disable garbage collection while the native code accesses the contents of primitive arrays. Programmers must apply the same kind of care as when using Get/ReleaseStringCritical functions (§3.2.4). Between a pair of Get/ReleasePrimitiveArrayCritical functions, the native code must not call arbitrary JNI functions, or perform any blocking operations that may cause the application to deadlock.
Table 3.2 is a summary of all JNI functions related to primitive arrays. Java 2 SDK release 1.2 adds a number of new functions that enhance performance for certain array operations. The added functions do not support new operations other than bringing performance improvements.
Summary of JNI Primitive Array Functions
Figure 3.3 illustrates how a programmer may choose among JNI functions for accessing primitive arrays in JDK release 1.1 and Java 2 SDK release 1.2:
Figure 3.3 Choosing among Primitive Array Functions
If you need to copy to or copy from a preallocated C buffer, use the Get/Set<Type>ArrayRegion family of functions. These functions perform bounds checking and raise ArrayIndexOutOfBoundsException exceptions when necessary. The native method implementation in Section 3.3.1 uses GetIntArray-Region to copy 10 elements out of a jarray reference.
For small, fixed-size arrays, Get/Set<Type>ArrayRegion is almost always the preferred function because the C buffer can be allocated on the C stack very cheaply. The overhead of copying a small number of array elements is negligible.
The Get/Set<Type>ArrayRegion functions allow you to specify a starting index and number of elements, and are thus the preferred functions if the native code needs to access only a subset of elements in a large array.
If you do not have a preallocated C buffer, the primitive array is of undetermined size and the native code does not issue blocking calls while holding the pointer to array elements, use the Get/ReleasePrimitiveArrayCritical functions in Java 2 SDK release 1.2. Just like the Get/ReleaseStringCritical functions, the Get/ReleasePrimitiveArrayCritical functions must be used with extreme care in order to avoid deadlocks.
It is always safe to use the Get/Release<type>ArrayElements family of functions. The virtual machine either returns a direct pointer to the array elements, or returns a buffer that holds a copy of the array elements.
The JNI provides a separate pair of functions to access objects arrays. GetObject-ArrayElement returns the element at a given index, whereas SetObjectArray-Element updates the element at a given index. Unlike the situation with primitive array types, you cannot get all the object elements or copy multiple object elements at once.
Strings and arrays are of reference types. You use Get/SetObjectArray-Element to access arrays of strings and arrays of arrays.
The following example calls a native method to create a two-dimensional array of int and then prints the content of the array.
class ObjectArrayTest {
private static native int[][] initInt2DArray(int size);
public static void main(String[] args) {
int[][] i2arr = initInt2DArray(3);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.print(" " + i2arr[i][j]);
}
System.out.println();
}
}
static {
System.loadLibrary("ObjectArrayTest");
}
}
The static native method initInt2DArray creates a two-dimensional array of the given size. The native method that allocates and initializes the two-dimensional array may be written as follows:
JNIEXPORT jobjectArray JNICALL
Java_ObjectArrayTest_initInt2DArray(JNIEnv *env,
jclass cls,
int size)
{
jobjectArray result;
int i;
jclass intArrCls = (*env)->FindClass(env, "[I");
if (intArrCls == NULL) {
return NULL; /* exception thrown */
}
result = (*env)->NewObjectArray(env, size, intArrCls,
NULL);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
for (i = 0; i < size; i++) {
jint tmp[256]; /* make sure it is large enough! */
int j;
jintArray iarr = (*env)->NewIntArray(env, size);
if (iarr == NULL) {
return NULL; /* out of memory error thrown */
}
for (j = 0; j < size; j++) {
tmp[j] = i + j;
}
(*env)->SetIntArrayRegion(env, iarr, 0, size, tmp);
(*env)->SetObjectArrayElement(env, result, i, iarr);
(*env)->DeleteLocalRef(env, iarr);
}
return result;
}
The newInt2DArray method first calls the JNI function FindClass to obtain a reference of the element class of the two-dimensional int array. The "[I" argument to FindClass is the JNI class descriptor (§12.3.2) that corresponds to the int[] type in the Java programming language. FindClass returns NULL and throws an exception if class loading fails (due to, for example, a missing class file or an out-of-memory condition).
Next the NewObjectArray function allocates an array whose element type is denoted by the intArrCls class reference. The NewObjectArray function only allocates the first dimension, and we are still left with the task of filling in the array elements that constitute the second dimension. The Java virtual machine has no special data structure for multi-dimensional arrays. A two-dimensional array is simply an array of arrays.
The code that creates the second dimension is quite straightforward. NewInt-Array allocates the individual array elements, and SetIntArrayRegion copies the contents of the tmp[] buffer into the newly allocated one-dimensional arrays. After completing the SetObjectArrayElement call, the jth element of the ith one-dimensional array has value i+j.
Running the ObjectArrayTest.main method produces the following output:
0 1 2 1 2 3 2 3 4
The DeleteLocalRef call at the end of the loop ensures that the virtual machine does not run out of the memory used to hold JNI references such as iarr. Section 5.2.1 explains in detail when and why you need to call DeleteLocalRef.
| Contents | Prev | Next | Index | The Java Native Interface Programmer's Guide and Specification |
Copyright © 2002 Sun Microsystems, Inc.
All rights reserved
Please send any comments or corrections to jni@java.sun.com