JNI has been enhanced in the Java SE to:
#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 */
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 SE 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.
In the JDK, 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:
To facilitate versioning control and resource management, JNI libraries in the Java Platform may optionally export the following two functions:
jint
JNI_OnLoad(JavaVM *vm, void *reserved);
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);
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.
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 JDK 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 JDK, 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);
ref
. The given ref
may be a global or
local reference. Returns NULL
if ref
refers to null
.
jboolean
ExceptionCheck(JNIEnv *env);
JNI_TRUE
when there is a pending exception;
otherwise, returns JNI_FALSE
.
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 SE 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 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);
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);
In JDK 1.1, programmers can use
Get/Release<PrimitiveType>ArrayElements
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);
Get/Release<PrimitiveType>ArrayElements
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.
void
GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len,
jchar *buf);
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);
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);
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
.
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);
java.lang.reflect.Method
or
java.lang.reflect.Constructor
object to a method ID.
jfieldID FromReflectedField(JNIEnv *env, jobject
field);
java.lang.reflect.Field
to a field ID.
jobject ToReflectedMethod(JNIEnv *env, jclass cls,
jmethodID methodID);
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);
cls
to a
java.lang.reflect.Field
object.
Throws OutOfMemoryError
and returns 0 if fails.
jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void
*args);
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 JDK, 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 JDK 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 JDK:
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 JDK, there is no longer any need to call * JNI_GetDefaultJavaVMInitArgs. */ res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args); if (res < 0) ...
The JDK still supports JDK1_1InitArgs
in
exactly the same way as JDK 1.1.
jint AttachCurrentThread(JavaVM *vm, void **penv, void
*args);
AttachCurrentThread
is always a pointer to JNIEnv
. The third argument to
AttachCurrentThread
was reserved, and should be set to
NULL
.
In the JDK, 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);
DestroyJavaVM
to unload the entire VM.
In the JDK, the main thread can be detached from the VM.
jint DestroyJavaVM(JavaVM *vm);
DestroyJavaVM
was not complete in 1.1.
Only the main thread may call DestroyJavaVM
. In the
JDK, 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
JDK still does not support VM unloading, however.
DestroyJavaVM
always returns an error code.
jint GetEnv(JavaVM *vm,
void **env, jint version);
*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
.