JNI Enhancements in the Java 2 SDK

# JNI Enhancements
Introduced in version 1.2 of the JavaTM 2 SDK

JNI Documentation

JNI has been enhanced in the Java 2 SDK to:

- incorporate new features in the evolving Java platform,
- ensure an efficient and stable interface for Java/native code integration.

Changes to the JNI are made based on feedback from users . Please continue to send your comments, suggestions, and concerns to jni@java.sun.com.

## New Constants

>     #define JNI_VERSION_1_1 0x00010001
>     #define JNI_VERSION_1_2 0x00010002

>     /* Error codes */
>     #define JNI_EDETACHED    (-2)              /* thread detached from the VM */
>     #define JNI_EVERSION     (-3)              /* JNI version error */

## Enhancements to the Existing API

- Class Operations
  - `FindClass`
- Invocation API
  - `JNI_CreateJavaVM`
  - `AttachCurrentThread`
  - `DetachCurrentThread`
  - `DestroyJavaVM`

## New Functions

- Library and Version Management
  - `JNI_OnLoad`
  - `JNI_OnUnload`
- Local Reference Management
  - `EnsureLocalCapacity`
  - `PushLocalFrame`
  - `PopLocalFrame`
  - `NewLocalRef`
- Exceptions
  - `ExceptionCheck`
- Weak Global References
  - `NewWeakGlobalRef`
  - `DeleteWeakGlobalRef`
- Array Operations
  - `GetPrimitiveArrayCritical`
  - `ReleasePrimitiveArrayCritical`
- String Operations
  - `GetStringRegion`
  - `GetStringUTFRegion`
  - `GetStringCritical`
  - `ReleaseStringCritical`
- Reflection Support
  - `FromReflectedMethod`
  - `FromReflectedField`
  - `ToReflectedMethod`
  - `ToReflectedField`
- Invocation API
  - `GetEnv`

## Class Operations

In the enhanced Java security model, non-system classes can load native code. The JNI function `FindClass` has been extended so that it finds classes loaded with a class loader.

`jclass FindClass(JNIEnv *env, const char *name);`

> In JDK 1.1, `FindClass` searched only local classes in `CLASSPATH`. The resulting classes did not have a class loader.

> The Java security model has been extended to allow non-system classes to load and call native methods. In the Java 2 Platform, `FindClass` locates the class loader associated with the current native method. If the native code belongs to a system class, no class loader will be involved. Otherwise, the proper class loader will be invoked to load and link the named class.

> When `FindClass` is called through the Invocation Interface, there is no current native method or its associated class loader. In that case, the result of `ClassLoader.getBaseClassLoader` is used. This is the class loader the virtual machine creates for applications, and is able to locate classes listed in the `java.class.path` property.

## Library and Version Management

In JDK 1.1, once a native library is loaded, it is visible from all class loaders. Therefore two classes in different class loaders may link with the same native method. This leads to two problems:

- A class may mistakenly link with native libraries loaded by a class with the same name in a different class loader.
- Native methods can easily mix classes from different class loaders. This breaks the name space separation offered by class loaders, and leads to type safety problems.

In the Java 2 SDK, each class loader manages its own set of native libraries. The same JNI native library cannot be loaded into more than one class loader. Doing so causes `UnsatisfiedLinkError` to be thrown. For example, `System.loadLibrary` throws an `UnsatisfiedLinkError` when used to load a native library into two class loaders. The benefits of the new approach are:

- Name space separation based on class loaders is preserved in native libraries. A native library cannot easily mix classes from different class loaders.
- In addition, native libraries can be unloaded when their corresponding class loaders are garbage collected.

To facilitate versioning control and resource management, JNI libraries in the Java 2 Platform may optionally export the following two functions:

`jint JNI_OnLoad(JavaVM *vm, void *reserved);`

> The VM calls `JNI_OnLoad` when the native library is loaded (for example, through `System.loadLibrary`). `JNI_OnLoad` must return the JNI version needed by the native library.

> In order to use any of the new JNI functions, a native library must export a `JNI_OnLoad` function that returns `JNI_VERSION_1_2`. If the native library does not export a `JNI_OnLoad` function, the VM assumes that the library only requires JNI version `JNI_VERSION_1_1`. If the VM does not recognize the version number returned by `JNI_OnLoad`, the native library cannot be loaded.

`void JNI_OnUnload(JavaVM *vm, void *reserved);`

> The VM calls `JNI_OnUnload` when the class loader containing the native library is garbage collected. This function can be used to perform cleanup operations. Because this function is called in an unknown context (such as from a finalizer), the programmer should be conservative on using Java VM services, and refrain from arbitrary Java call-backs.

Note that `JNI_OnLoad` and `JNI_OnUnload` are two functions optionally supplied by JNI libraries, not exported from the VM.

## Local Reference Management

Local references are valid for the duration of a native method call. They are freed automatically after the native method returns. Each local reference costs some amount of Java Virtual Machine resource. Programmers need to make sure that native methods do not excessively allocate local references. Although local references are automatically freed after the native method returns to Java, excessive allocation of local references may cause the VM to run out of memory during the execution of a native method.

JDK 1.1 provides a `DeleteLocalRef` function so that programmers can manually delete local references. For example, if native code iterates through a potentially large array of objects and uses one element in each iteration, it is a good practice to delete the local reference to the no-longer-used array element before a new local reference is created in the next iteration.

The Java 2 SDK provides an additional set of functions for local reference lifetime management.

`jint EnsureLocalCapacity(JNIEnv *env, jint capacity);`

> Ensures that at least a given number of local references can be created in the current thread. Returns 0 on success; otherwise returns a negative number and throws an `OutOfMemoryError`.

> Before it enters a native method, the VM automatically ensures that at least 16 local references can be created.

> For backward compatibility, the VM allocates local references beyond the ensured capacity. (As a debugging support, the VM may give the user warnings that too many local references are being created. In the Java 2 SDK, the programmer can supply the `-verbose:jni` command line option to turn on these messages.) The VM calls `FatalError` if no more local references can be created beyond the ensured capacity.

`jint PushLocalFrame(JNIEnv *env, jint capacity);`

> Creates a new local reference frame, in which at least a given number of local references can be created. Returns 0 on success, a negative number and a pending `OutOfMemoryError` on failure.

> Note that local references already created in previous local frames are still valid in the current local frame.

`jobject PopLocalFrame(JNIEnv *env, jobject result);`

> Pops off the current local reference frame, frees all the local references, and returns a local reference in the previous local reference frame for the given `result` object.

> Pass `NULL` as `result` if you do not need to return a reference to the previous frame.

`jobject NewLocalRef(JNIEnv *env, jobject ref);`

> Creates a new local reference that refers to the same object as `ref`. The given `ref` may be a global or local reference. Returns `NULL` if `ref` refers to `null`.

## Exceptions

We introduce a convenience function to check for pending exceptions without creating a local reference to the exception object.

`jboolean ExceptionCheck(JNIEnv *env);`

> Returns `JNI_TRUE` when there is a pending exception; otherwise, returns `JNI_FALSE`.

## Weak Global References

Weak global references are a special kind of global reference. Unlike normal global references, a weak global reference allows the underlying Java object to be garbage collected. Weak global references may be used in any situation where global or local references are used. When the garbage collector runs, it frees the underlying object if the object is only referred to by weak references. A weak global reference pointing to a freed object is functionally equivalent to `NULL`. Programmers can detect whether a weak global reference points to a freed object by using `IsSameObject` to compare the weak reference against `NULL`.

Weak global references in JNI are a simplified version of the Java Weak References, available as part of the Java 2 Platform API ( `java.lang.ref` package and its classes).

Clarification (added June 2001)

> Since garbage collection may occur while native methods are running, objects referred to by weak global references can be freed at any time. While weak global references can be used where global references are used, it is generally inappropriate to do so, as they may become functionally equivalent to

> `NULL` without notice.

> While

> `IsSameObject` can be used to determine whether a weak global reference refers to a freed object, it does not prevent the object from being freed immediately thereafter. Consequently, programmers may not rely on this check to determine whether a weak global reference may used (as a non-`NULL` reference) in any future JNI function call.

> To overcome this inherent limitation, it is recommended that a standard (strong) local or global reference to the same object be acquired using the JNI functions

> `NewLocalRef` or `NewGlobalRef`, and that this strong reference be used to access the intended object. These functions will return `NULL` if the object has been freed, and otherwise will return a strong reference (which will prevent the object from being freed). The new reference should be explicitly deleted when immediate access to the object is no longer required, allowing the object to be freed.

> The weak global reference is weaker than other types of weak references (Java objects of the SoftReference or WeakReference classes). A weak global reference to a specific object will not become functionally equivalent to

> `NULL` until after SoftReference or WeakReference objects referring to that same specific object have had their references cleared.

> The weak global reference is weaker than Java's internal references to objects requiring finalization. A weak global reference will not become functionally equivalent to

> `NULL` until after the completion of the the finalizer for the referenced object, if present.

> Interactions between weak global references and PhantomReferences are undefined. In particular, implementations of a Java VM may (or may not) process weak global references after PhantomReferences, and it may (or may not) be possible to use weak global references to hold on to objects which are also referred to by PhantomReference objects. This undefined use of weak global references should be avoided.

`jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);`

> Creates a new weak global reference. Returns `NULL` if `obj` refers to `null`, or if the VM runs out of memory. If the VM runs out of memory, an `OutOfMemoryError` will be thrown.

`void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);`

> Delete the VM resources needed for the given weak global reference.

## Array Operations

In JDK 1.1, programmers can use `Get/ReleaseArrayElements` functions to obtain a pointer to primitive array elements. If the VM supports pinning, the pointer to the original data is returned; otherwise, a copy is made.

New functions allow native code to obtain a direct pointer to array elements even if the VM does not support pinning.

`void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);`

> The semantics of these two functions are very similar to the existing `Get/ReleaseArrayElements` functions. If possible, the VM returns a pointer to the primitive array; otherwise, a copy is made. However, there are significant restrictions on how these functions can be used.

> After calling `GetPrimitiveArrayCritical`, the native code should not run for an extended period of time before it calls `ReleasePrimitiveArrayCritical`. We must treat the code inside this pair of functions as running in a "critical region." Inside a critical region, native code must not call other JNI functions, or any system call that may cause the current thread to block and wait for another Java thread. (For example, the current thread must not call `read` on a stream being written by another Java thread.)

> These restrictions make it more likely that the native code will obtain an uncopied version of the array, even if the VM does not support pinning. For example, a VM may temporarily disable garbage collection when the native code is holding a pointer to an array obtained via `GetPrimitiveArrayCritical`.

> Multiple pairs of `GetPrimtiveArrayCritical` and `ReleasePrimitiveArrayCritical` may be nested. For example:

>       jint len = (*env)->GetArrayLength(env, arr1);
>       jbyte *a1 = (*env)->GetPrimitiveArrayCritical(env, arr1, 0);
>       jbyte *a2 = (*env)->GetPrimitiveArrayCritical(env, arr2, 0);
>       /* We need to check in case the VM tried to make a copy. */
>       if (a1 == NULL || a2 == NULL) {
>         ... /* out of memory exception thrown */
>       }
>       memcpy(a1, a2, len);
>       (*env)->ReleasePrimitiveArrayCritical(env, arr2, a2, 0);
>       (*env)->ReleasePrimitiveArrayCritical(env, arr1, a1, 0);

Note that `GetPrimitiveArrayCritical` might still make a copy of the array if the VM internally represents arrays in a different format. Therefore we need to check its return value against `NULL` for possible out of memory situations.

## String Operations

In JDK 1.1, programmers can get primitive array elements in a user-supplied buffer. The Java 2 SDK allows native code to obtain Unicode or UTF-8 characters in a user-supplied buffer.

`void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);`

> Copies `len` number of Unicode characters beginning at offset `start` to the given buffer `buf`.

> Throws `StringIndexOutOfBoundsException` on index overflow.

< `void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);`

> Translates `len` number of Unicode characters beginning at offset `start` into UTF-8 format and place the result in the given buffer `buf`.

> Throws `StringIndexOutOfBoundsException` on index overflow.

`const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);`

> The semantics of these two functions are similar to the existing `Get/ReleaseStringChars` functions. If possible, the VM returns a pointer to string elements; otherwise, a copy is made. However, there are significant restrictions on how these functions can be used. In a code segment enclosed by `Get/ReleaseStringCritical` calls, the native code must not issue arbitrary JNI calls, or cause the current thread to block.

> The restrictions on `Get/ReleaseStringCritical` are similar to those on `Get/ReleasePrimitiveArrayCritical`.

## Reflection Support

Programmers can use the JNI to call Java methods or access Java fields if they know the name and type of the methods or fields. The Java Core Reflection API allows programmers to introspect Java classes at runtime. JNI provides a set of conversion functions between field and method IDs used in the JNI to field and method objects used in the Java Core Reflection API.

`jmethodID FromReflectedMethod(JNIEnv *env, jobject method);`

> Converts a `java.lang.reflect.Method` or `java.lang.reflect.Constructor` object to a method ID.

`jfieldID FromReflectedField(JNIEnv *env, jobject field);`

> Converts a `java.lang.reflect.Field` to a field ID.

`jobject ToReflectedMethod(JNIEnv *env, jclass cls,
jmethodID methodID);`

> Converts a method ID derived from `cls` to a `java.lang.reflect.Method` or `java.lang.reflect.Constructor` object.

> Throws `OutOfMemoryError` and returns 0 if fails.

`jobject ToReflectedField(JNIEnv *env, jclass cls,
jfieldID fieldID);`

> Converts a field ID derived from `cls` to a `java.lang.reflect.Field` object.

> Throws `OutOfMemoryError` and returns 0 if fails.

## Invocation API

`jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);`

> In JDK 1.1, the second argument to `JNI_CreateJavaVM` is always a pointer to `JNIEnv *`. The third argument is a pointer to a JDK 1.1 specific structure (`JDK1_1InitArgs`). The `JDK1_1InitArgs` structure is clearly not designed to be portable on all VMs.

> In the Java 2 SDK, we introduce a standard VM initialization structure. Backward compatibility is preserved. If the VM initialization argument points to a `JDK1_1InitArgs` structure, `JNI_CreateJavaVM` still returns the 1.1 version of JNI interface pointer. The VM returns the 1.2 version of JNI interface pointer if the third argument points to a `JavaVMInitArgs` structure. Unlike `JDK1_1InitArgs`, which contains a fixed set of options, `JavaVMInitArgs` uses option strings to encode arbitrary VM start up options.

>     typedef struct JavaVMInitArgs {
>         jint version;

>         jint nOptions;
>         JavaVMOption *options;
>         jboolean ignoreUnrecognized;
>     } JavaVMInitArgs;

> The `version` field must be set to `JNI_VERSION_1_2`. (In contrast, the version field in `JDK1_1InitArgs` must be set to `JNI_VERSION_1_1`.) The `options` field is an array of the following type:

>     typedef struct JavaVMOption {
>         char *optionString;
>         void *extraInfo;
>     } JavaVMOption;

> The size of the array is denoted by the nOptions field in `JavaVMInitArgs`. If `ignoreUnrecognized` is `JNI_TRUE`, `JNI_CreateJavaVM` ignore all unrecognized option strings that begin with "`-X`" or "`_`". If `ignoreUnrecognized` is `JNI_FALSE`, `JNI_CreateJavaVM` returns `JNI_ERR` as soon as it encounters any unrecognized option strings. All Java VMs must recognize the following set of standard options:

> optionString | meaning
> `-D<name>=<value>` | Set a system property
> `-verbose[:class|gc|jni]` | Enable verbose output. The options can be followed by a comma-separated list of names indicating what kind of messages will be printed by the VM. For example, "`-verbose:gc,class`" instructs the VM to print GC and class loading related messages. Standard names include: `gc`, `class`, and `jni`. All nonstandard (VM-specific) names must begin with "`X`".
> `vfprintf` | `extraInfo` is a pointer to the `vfprintf` hook.
> `exit` | `extraInfo` is a pointer to the `exit` hook.
> `abort` | `extraInfo` is a pointer to the `abort` hook.

> In addition, each VM implementation may support its own set of non-standard option strings. Non-standard option names must begin with "`-X`" or an underscore ("`_`"). For example, the Java 2 SDK supports `-Xms` and `-Xmx` options to allow programmers specify the initial and maximum heap size. Options that begin with "`-X`" are accessible from the "`java`" command line.

> Here is the example code that creates a Java VM in the Java 2 SDK:

>     JavaVMInitArgs vm_args;
>     JavaVMOption options[4];

>     options[0].optionString = "-Djava.compiler=NONE";           /* disable JIT */
>     options[1].optionString = "-Djava.class.path=c:\myclasses"; /* user classes */
>     options[2].optionString = "-Djava.library.path=c:\mylibs";  /* set native library path */
>     options[3].optionString = "-verbose:jni";                   /* print JNI-related messages */

>     vm_args.version = JNI_VERSION_1_2;
>     vm_args.options = options;
>     vm_args.nOptions = 4;
>     vm_args.ignoreUnrecognized = TRUE;

>     /* Note that in the Java 2 SDK, there is no longer any need to call
>      * JNI_GetDefaultJavaVMInitArgs.
>      */
>     res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
>     if (res < 0) ...

> The Java 2 SDK still supports `JDK1_1InitArgs` in exactly the same way as JDK 1.1.

`jint AttachCurrentThread(JavaVM *vm, void **penv, void *args);`

> In JDK 1.1, the second argument to `AttachCurrentThread` is always a pointer to `JNIEnv`. The third argument to `AttachCurrentThread` was reserved, and should be set to `NULL`.

> In the Java 2 SDK, you pass `NULL` as the third argument for 1.1 behavior, or pass a pointer to the following structure to specify additional information:

> >     typedef struct JavaVMAttachArgs {
> >         jint version;  /* must be JNI_VERSION_1_2 */
> >         char *name;    /* the name of the thread, or NULL */
> >         jobject group; /* global ref of a ThreadGroup object, or NULL */
> >     } JavaVMAttachArgs;

`jint DetachCurrentThread(JavaVM *vm);`

> In JDK 1.1, the main thread cannot be detached from the VM. It must call `DestroyJavaVM` to unload the entire VM.

> In the Java 2 SDK, the main thread can be detached from the VM.

`jint DestroyJavaVM(JavaVM *vm);`

> The support for `DestroyJavaVM` was not complete in 1.1. Only the main thread may call `DestroyJavaVM`. In the Java 2 SDK, any thread, whether attached or not, can call this function. If the current thread is attached, the VM waits until the current thread is the only user-level Java thread. If the current thread is not attached, the VM attaches the current thread and then waits until the current thread is the only user-level thread. The Java 2 SDK still does not support VM unloading, however. `DestroyJavaVM` always returns an error code.

`jint GetEnv(JavaVM *vm, void **env, jint version);`

> If the current thread is not attached to the VM, sets `*env` to `NULL`, and returns `JNI_EDETACHED`. If the specified version is not supported, sets `*env` to `NULL`, and returns `JNI_EVERSION`. Otherwise, sets `*env` to the appropriate interface, and returns `JNI_OK`.

Copyright © 1995-99 Sun Microsystems, Inc. All Rights Reserved. Please send comments to: jni@java.sun.com |