JNI transitions

To understand JNI local reference management and the GC, you must understand the context of a running thread attached to the JVM. Every thread has a runtime stack that includes a frame for each method call. From a GC perspective, every stack establishes a thread-specific "root set" including the union of all JNI local references in the stack.

This picture shows an example stack for a JNI application

Each method call in a running VM adds (pushes) a frame onto the stack, just as every return removes (pops) a frame. Each call point in a running stack can be characterized as one of the following types:

  • Java™ to Java (J2J)
  • Native to Native (N2N)
  • Java to Native (J2N)
  • Native to Java (N2J)

You can only perform an N2J transition in a thread that meets the following conditions:

  • The process containing the thread must contain a JVM started using the JNI Invocation API.
  • The thread must be "attached" to the JVM.
  • The thread must pass at least one valid local or global object reference to JNI.

J2J and N2N transitions

Because object references do not change form as part of J2J or N2N transitions, J2J and N2N transitions do not affect JNI local reference management.

Any section of N2N code that obtains many local references without promptly returning to Java can needlessly stress the local reference capacity of a thread. This problem can be avoided if local references are managed explicitly by the native method programmer.

N2J transitions

For native code to call Java code (N2J) in the current thread, the thread must first be attached to the JVM in the current process.

Every N2J call that passes object references must have obtained them using JNI, therefore they are either valid local or global JNI refs. Any object references returned from the call are JNI local references.

J2N calls

The JVM must ensure that objects passed as parameters from Java to the native method and any new objects created by the native code remain reachable by the GC. To handle the GC requirements, the JVM allocates a small region of specialized storage called a local reference root set.

A local reference root set is created when:

  • A thread is first attached to the JVM (the outermost root set of the thread).
  • Each J2N transition occurs.
The JVM initializes the root set created for a J2N transition with:
  • A local reference to the caller's object or class.
  • A local reference to each object passed as a parameter to the native method.
New local references created in native code are added to this J2N root set, unless you create a new local frame using the PushLocalFrame JNI function.
The default root set is large enough to contain 16 local references per J2N transition. The -Xcheck:jni command-line option causes the JVM to monitor JNI usage. When -Xcheck:jni is used, the JVM writes a warning message when more than 16 local references are required at run time. If you receive this warning message, use one of the following JNI functions to manage local references more explicitly:
  • NewLocalRef
  • DeleteLocalRef
  • PushLocalFrame
  • PopLocalFrame
  • EnsureLocalCapacity

J2N returns

When native code returns to Java, the associated JNI local reference "root set", created by the J2N call, is released.

If the JNI local reference was the only reference to an object, the object is no longer reachable and can be considered for garbage collection. Garbage collection is triggered automatically by this condition, which simplifies memory management for the JNI programmer.