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.
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.
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.
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 situations 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 and its related classes).
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.
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.
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.
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.
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:
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.
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.
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.