Java Native Interface Specification


The Java Native Interface (JNI) allows JVMs to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly.

The following examples illustrate when you need to use Java native methods:

  • The standard Java class library does not support the platform-dependent features needed by the application.
  • You already have a library written in another language, and wish to make it accessible to Java code through the JNI.
  • You want to implement a small portion of time-critical code in a lower-level language such as assembly.
By programming through the JNI, you can use native methods to:

  • Create, inspect, and update Java objects (including arrays and strings).
  • Call Java methods.
  • Catch and throw exceptions.
  • Load classes and obtain class information.
  • Perform runtime type checking.
You can also use the JNI with the Invocation API to enable an arbitrary native application to embed the Java VM. This allows programmers to easily make their existing applications Java-enabled without having to link with the VM source code.


Background

Currently, VMs from different vendors offer different native method interfaces. These different interfaces force programmers to produce, maintain, and distribute multiple versions of native method libraries on a given platform.

We briefly examine some of the existing native method interfaces, such as:

  • JDK 1.0 native method interface
  • Netscape's Java Runtime Interface
  • Microsoft's Raw Native Interface and Java/COM interface


JDK 1.0 Native Method Interface

JDK 1.0 shipped with a native method interface. Unfortunately, there are two major reasons that this interface is unsuitable for adoption by other Java VMs.

First, the native code accesses fields in Java objects as members of C structures. However, the Java Language Specification does not define how objects are laid out in memory. If a Java VM lays out objects differently in memory, then the programmer would have to recompile the native method libraries.

Second, JDK 1.0's native method interface relies on a conservative garbage collector. The unrestricted use of the unhand macro, for example, makes it necessary to conservatively scan the native stack.



Java Runtime Interface

Netscape proposed the Java Runtime Interface (JRI), a general interface for services provided in the Java virtual machine. JRI is designed with portability in mind---it makes few assumptions about the implementation details in the underlying Java VM. The JRI addresses a wide range of issues, including native methods, debugging, reflection, embedding (invocation), and so on.



Raw Native Interface and Java/COM Interface

The Microsoft Java VM supports two native method interfaces. At the low level, it provides an efficient Raw Native Interface (RNI). The RNI offers a high degree of source-level backward compatibility with the JDK's native method interface, although it has one major difference. Instead of relying on conservative garbage collection, the native code must use RNI functions to interact explicitly with the garbage collector.

At a higher level, Microsoft's Java/COM interface offers a language- independent standard binary interface to the Java VM. Java code can use a COM object as if it were a Java object. A Java class can also be exposed to the rest of the system as a COM class.



Objectives

We believe that a uniform, well-thought-out standard interface offers the following benefits for everyone:

  • Each VM vendor can support a larger body of native code.
  • Tool builders will not have to maintain different kinds of native method interfaces.
  • Application programmers will be able to write one version of their native code and this version will run on different VMs.
The best way to achieve a standard native method interface is to involve all parties with an interest in Java VMs. Therefore we organized a series of discussions among the Java licensees on the design of a uniform native method interface. It is clear from the discussions that the standard native method interface must satisfy the following requirements:

  • Binary compatibility - The primary goal is binary compatibility of native method libraries across all Java VM implementations on a given platform. Programmers should maintain only one version of their native method libraries for a given platform.
  • Efficiency - To support time-critical code, the native method interface must impose little overhead. All known techniques to ensure VM-independence (and thus binary compatibility) carry a certain amount of overhead. We must somehow strike a compromise between efficiency and VM- independence.
  • Functionality - The interface must expose enough Java VM internals to allow native methods to accomplish useful tasks.


Java Native Interface Approach

We hoped to adopt one of the existing approaches as the standard interface, because this would have imposed the least burden on programmers who had to learn multiple interfaces in different VMs. Unfortunately, no existing solutions are completely satisfactory in achieving our goals.

Netscape's JRI is the closest to what we envision as a portable native method interface, and was used as the starting point of our design. Readers familiar with the JRI will notice the similarities in the API naming convention, the use of method and field IDs, the use of local and global references, and so on. Despite our best efforts, however, the JNI is not binary-compatible with the JRI, although a VM can support both the JRI and the JNI.

Microsoft's RNI is an improvement over JDK 1.0 because it solves the problem of native methods working with a nonconservative garbage collector. The RNI, however, is not suitable as a VM-independent native method interface. Like the JDK, RNI native methods access Java objects as C structures. This leads to two problems:

  • RNI exposes the layout of internal Java objects to native code.
  • Direct access of Java objects as C structures makes it impossible to efficiently incorporate "write barriers," which are necessary in advanced garbage collection algorithms.
As a binary standard, COM ensures complete binary compatibility across different VMs. Invoking a COM method requires only an indirect call, which carries little overhead. In addition, COM objects are a great improvement over dynamic-link libraries in solving versioning problems.

The use of COM as the standard Java native method interface, however, is hampered by a few factors:

  • First, the Java/COM interface lacks certain desired functions, such as accessing private fields and raising general exceptions.
  • Second, the Java/COM interface automatically provides the standard IUnknown and IDispatch COM interfaces for Java objects, so that native code can access public methods and fields. Unfortunately, the IDispatch interface does not deal with overloaded Java methods and is case- insensitive in matching method names. Furthermore, all Java methods exposed through the IDispatch interface are wrapped to perform dynamic type checking and coercion. This is because the IDispatch interface is designed with weakly-typed languages (such as Basic) in mind.
  • Third, instead of dealing with individual low-level functions, COM is designed to allow software components (including full-fledged applications) to work together. We believe that it is not appropriate to treat all Java classes or low-level native methods as software components.
  • Fourth, the immediate adoption of COM is hampered by the lack of its support on UNIX platforms.
Although we do not expose Java objects to the native code as COM objects, the JNI interface itself is binary-compatible with COM. We use the same jump table structure and calling convention that COM does. This means that, as soon as cross-platform support for COM is available, the JNI can become a COM interface to the Java VM.

We do not believe that the JNI should be the only native method interface supported by a given Java VM. A standard interface benefits programmers who would like to load their native code libraries into different Java VMs. In some cases, the programmer may have to use a lower-level, VM-specific interface to achieve top efficiency. In other cases, the programmer might use a higher-level interface to build software components. Indeed, we hope that, as the Java environment and component software technologies become more mature, native methods will gradually lose their significance.



Programming to the JNI

Native method programmers should start programming to the JNI. Programming to the JNI insulates you from unknowns, such as the vendor's VM that the end user might be running. By conforming to the JNI standard, you will give a native library the best chance to run in a given Java VM. For example, although JDK 1.1 will continue to support the old-style native method interface that was implemented in JDK 1.0, it is certain that future versions of the JDK will stop supporting the old-style native method interface. Native methods relying on the old-style interface will have to be rewritten.

If you are implementing a Java VM, you should implement the JNI. We (Javasoft and the licensees) have tried our best to ensure that the JNI does not impose any overhead or restrictions on your VM implementation, including object representation, garbage collection scheme, and so on. Please let us know if you run into any problems we might have overlooked.



Changes in JDK 1.1.2

To better support the Java Runtime Environment (JRE), the Invocation API has been extended in JDK 1.1.2 in a few minor ways. The changes do not break any existing code. The JNI Native Method Interface has not been changed.

  • The reserved0 field in the JDK1_1InitArgs structure has been renamed to version. The JDK1_1InitArgs structure holds the initialization arguments to JNI_CreateJavaVM. Callers of JNI_GetDefaultJavaVMInitArgs and JNI_CreateJavaVM must set the version field to 0x00010001. JNI_GetDefaultJavaVMInitArgs has been changed to return a jint indicating whether the requested version is supported.
  • The reserved1 field in the JDK1_1InitArgs structure has been renamed to properties. This is a NULL-terminated array of strings. Each string has the format:

    name=value
    indicating a system property. (This facility corresponds to the -D option in the java command line.)
  • In JDK 1.1.1, the thread calling DestroyJavaVM must be the only user thread in the VM. JDK 1.1.2 has lifted this restriction. If DestroyJavaVM is called when there is more than one user thread, the VM waits until the current thread is the only user thread, and then tries to destroy itself.

*As used on this web site, the terms "Java Virtual Machine" or "JVM" mean a virtual machine for the Java platform.


Contents | Prev | Next

Java Native Interface Specification (HTML generated by dkramer on March 15, 1997)
All rights reserved
Please send any comments or corrections to jni@java.sun.com