Copying and pinning

The GC might, at any time, decide it needs to compact the garbage-collected heap. Compaction involves physically moving objects from one address to another. These objects might be referred to by a JNI local or global reference. To allow compaction to occur safely, JNI references are not direct pointers to the heap. At least one level of indirection isolates the native code from object movement.

If a native method needs to obtain direct addressability to the inside of an object, the situation is more complicated. The requirement to directly address, or pin, the heap is typical where there is a need for fast, shared access to large primitive arrays. An example might include a screen buffer. In these cases a JNI critical section can be used, which imposes additional requirements on the programmer, as specified in the JNI description for these functions. See the JNI specification for details.

  • GetPrimitiveArrayCritical returns the direct heap address of a Java™ array, disabling garbage collection until the corresponding ReleasePrimitiveArrayCritical is called.
  • GetStringCritical returns the direct heap address of a java.lang.String instance, disabling garbage collection until ReleaseStringCritical is called.

All other Get<PrimitiveType>ArrayElements interfaces return a copy that is unaffected by compaction.

When using the Balanced Garbage Collection Policy, the *Critical forms of the calls might not return a direct pointer into the heap, which is reflected in the isCopy flag. This behavior is due to an internal representation of larger arrays, where data might not be sequential. Typically, an array with storage that is less than 1/1000th of the heap, is returned as a direct pointer.

Using the isCopy flag

The JNI Get<Type> functions specify a pass-by-reference output parameter (jboolean *isCopy) that allows the caller to determine whether a given JNI call is returning the address of a copy or the address of the pinned object in the heap.

The Get<Type> and Release<Type> functions come in pairs:

  • GetStringChars and ReleaseStringChars
  • GetStringCritical and ReleaseStringCritical
  • GetStringUTFChars and ReleaseStringUTFChars
  • Get<PrimitiveType>ArrayElements and Release<PrimitiveType>ArrayElements
  • GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical

If you pass a non-null address as the isCopy parameter, the JNI function sets the jboolean value at that address to JNI_TRUE if the address returned is the address of a copy of the array elements and JNI_FALSE if the address points directly into the pinned object in the heap.

Except for the critical functions, the J9 VM always returns a copy. Copying eases the burden on the GC, because pinned objects cannot be compacted and complicate defragmentation.

To avoid leaks, you must:

  • Manage the copy memory yourself using the Get<Type>Region and Set<Type>Region functions.
  • Ensure that you free copies made by a Get<Type> function by calling the corresponding Release<Type> function when the copy is no longer needed.

Using the mode flag

When you call Release<Type>ArrayElements, the last parameter is a mode flag. The mode flag is used to avoid unnecessary copying to the Java heap when working with a copied array. The mode flag is ignored if you are working with an array that has been pinned.

You must call Release<Type> once for every Get<Type> call, regardless of the value of the isCopy parameter. This step is necessary because calling Release<Type> deletes JNI local references that might otherwise prevent garbage collection.

The possible settings of the mode flag are:
0
Update the data on the Java heap. Free the space used by the copy.
JNI_COMMIT
Update the data on the Java heap. Do not free the space used by the copy.
JNI_ABORT
Do not update the data on the Java heap. Free the space used by the copy.

The ‘0' mode flag is the safest choice for the Release<Type> call. Whether the copy of the data was changed or not, the heap is updated with the copy, and there are no leaks.

To avoid having to copy back an unchanged copy, use the JNI_ABORT mode value. If you alter the returned array, check the isCopy flag before using the JNI_ABORT mode value to "roll back" changes. This step is necessary because a pinning JVM leaves the heap in a different state than a copying JVM.

A generic way to use the isCopy and mode flags

Here is a generic way to use the isCopy and mode flags. It works with all JVMs and ensures that changes are committed and leaks do not occur.

To use the flags in a generic way, ensure that you:
  • Do not use the isCopy flag. Pass in null or 0.
  • Always set the mode flag to zero.

A complicated use of these flags is necessary only for optimization. If you use the generic way, you must still consider synchronization. See Synchronization.