GetPrimitiveArrayCritical
will block all existing garbage collectors. (The experimental Shenandoah collector will often not block.) Blocking the garbage collector will block all object allocation (once the garbage piles up).
Therefore, the rules of using GetPrimitiveArrayCritical
are as follows:
- Do not call any JNI functions. The documentation in various parts does not stress this point sufficiently, but it is a rule you must obey. The reason is that JNI functions may allocate memory, particularly local references. Because it is not documented which JNI functions allocate memory, or how much, you cannot call any of of them. Supposedly, the function EnsureLocalCapacity can pre-allocate local references to work around this issue, but nobody documented how to use it. Don't call JNI functions except GetPrimitiveArrayCritical, GetStringCritical, ReleasePrimitiveArrayCritical, and ReleaseStringCritical inside a critical region, or you will deadlock.
- Do not, in any other way, block on code that may need to allocate memory from the heap. This mostly forbids blocking on Java code running in the same VM. Conceivably (but I can't say for sure), you can block on Java code that does not allocate.
- You are allowed to call JNI functions from other threads, as long as you don't block waiting on those threads. The threads in which you call JNI functions may stall. See next point.
- Spending too much time in the critical region will stall other threads. Depending on how many threads you are running, and their allocation rate, the amount of time you can spend in the critical region can vary. In a single-threaded application, or in a multi-threaded one that does very few allocations, you can safely spend indefinite time in the critical region. In other situations, however, you will stall threads so much that the performance benefits of
GetPrimitiveArrayCritical
will be completely negated. Stalling, however, is safe from a correctness point of view (as opposed to deadlocking). - You may nest
Get*Critical
andRelease*Critical
methods, and they are thread-safe. - Check the return value for
null
and setmode
correctly, becauseGet*Critical
methods are allowed to fail and/or make copies, just likeGet*ArrayElements
. - If you are writing a library and are considering using
GetPrimitiveArrayCritical
, create a runtime option to useGet*ArrayElements
instead. Even if you do not experience the stalling that arises from GetPrimitiveArrayCritical, your users might.
The Java flag -Xcheck:jni
will warn you if you call JNI functions inside a critical region. Ignore the documentation that says it is sometimes ok to call JNI functions inside a critical region. It isn't.
The Java 8 flags -XX:+PrintJNIGCStalls -XX:+PrintGCDetails
will print useful log messages about stalled allocations and collections. The messages to look for can be gleaned from src/share/vm/memory/gcLocker.cpp
In Java 9, logging has changed. Turn on logging for gc and jni. The messages to look for can be gleaned from src/share/vm/gc/shared/gcLocker.cpp
More information:
- https://shipilev.net/jvm-anatomy-park/9-jni-critical-gclocker/
- The publicly-available information on this is scarce. I am currently petitioning responsible people to clean up the JNI specification or otherwise clear up the confusion.