Clarification on what the JNI spec means by nonmovable
Jon V.
sybersnake at gmail.com
Thu Sep 8 21:21:49 UTC 2016
Krystal,
re
http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#GetPrimitiveArrayCritical
Thanks for clearing up that Critical sections can be nested. “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,” wasn’t terribly clear and the code example doesn’t appear
everywhere.
The code example here:
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);
Looks unsafe because it would be possible to acquire a1 then not acquire a2
whereas throwing an exception and exiting the method would keep the lock on
a1.
On Thu, Sep 8, 2016 at 2:53 PM, Krystal Mok <rednaxelafx at gmail.com> wrote:
>
>
> On Thu, Sep 8, 2016 at 10:42 AM, Jon V. <sybersnake at gmail.com> wrote:
>
>> Thank you Kris.
>>
>> That confirms my understanding that the indirect pointers can be moved at
>> anytime. I’m accessing possible multiple heap objects inside of a single
>> JNI call and you aren’t allowed to call critical sections twice.
>>
>
> Wait... a JNI critical section isn't like a OS thread-synchronization
> critical section. It does support nesting. What is the access pattern
> you're looking at?
>
> c.f. http://docs.oracle.com/javase/7/docs/technotes/
> guides/jni/spec/functions.html#GetPrimitiveArrayCritical
> > Multiple pairs of GetPrimtiveArrayCritical and
> ReleasePrimitiveArrayCritical may be nested.
>
>
>> I’ve created a workaround of using getCrtiicalString as a way to stop GC
>> then directly access the heap memory of the other objects. This seems like
>> a safe way to do this.
>>
>
> Nope, the JNI API does not guarantee this to be safe. Doing this is
> essentially cheating on JVMs such as HotSpot that implements JNI critical
> sections via temporarily disabling the GC.
>
> JNI critical section only guarantees "critical access" to the specified
> object, and that doesn't necessarily extends to other objects.
>
> JNI critical section may be implemented via "object pinning" support in
> the VM. Pinning may happen at different granularities in different JVMs:
> * on object level: only the specified object is pinned;
> * on page / region level: all objects in the page or region in which the
> specified object resides is pinned as a whole;
> * on heap level: all objects in the whole GC heap is pinned.
>
> Disabling moving GCs from running is effectively pinning on the heap
> level. That's what HotSpot currently implements. But there's no guarantee
> that it'll stay that way in the future.
> JRockit, for example, supports object-level pinning. So
> your GetStringCritical trick is likely to break if you ever try that on
> JRockit.
>
>
>
>> I really wish JNI had another wrapper/macro we could use such as
>> JNI_QUICK_ENTRY that would prevent GC without having to use GCLocker. Some
>> way my JNI call could be considered “inVM” instead of “inNative” to keep GC
>> at bay.
>>
>> I'll have to leave this one to the HotSpot GC folks...
>
> - Kris
>
>
>> On Thu, Sep 8, 2016 at 1:30 PM, Krystal Mok <rednaxelafx at gmail.com>
>> wrote:
>>
>>> Hi Jon,
>>>
>>> On Thu, Sep 8, 2016 at 9:30 AM, Jon V. <sybersnake at gmail.com> wrote:
>>>
>>>> Hello everyone!
>>>>
>>>> I’m trying to document the exact behavior of direct pointer access of
>>>> passed arguments into JNI in HotSpot. I have a huge post on
>>>> StackOverflow
>>>> for posterity purposes as it seems to be a commonly misunderstood
>>>> behavior.
>>>>
>>>> http://stackoverflow.com/questions/39381339/understanding-sa
>>>> fe-access-of-jni-arguments
>>>>
>>>> from
>>>> https://docs.oracle.com/javase/7/docs/technotes/guides/jni/s
>>>> pec/design.html#wp16789
>>>>
>>>> The JNI spec says, “To implement local references, the Java VM creates a
>>>> registry for each transition of control from Java to a native method. A
>>>> registry maps nonmovable local references to Java objects, and keeps the
>>>> objects from being garbage collected.”
>>>>
>>>> "Nonmovable" refers to the "local JNI reference", not the Java object
>>> instance itself. What that gurantees is that, given a jobject (which is
>>> typedef'd as an opaque pointer), you can safely say that as long as this
>>> jobject is still in scope, the value of the jobject won't change. But that
>>> doesn't imply anything related whether or not the Java object it's
>>> referring to is pinned.
>>>
>>> The "registry map" here is just an abstract notion that doesn't
>>> necessarily materialize into any real data structures in some certain JVM
>>> implementations.
>>>
>>>
>>>> I’m trying to understand if “nonmovable” actually means that the
>>>> objects in
>>>> the map WILL NOT be compacted/moved (heap memory) or if it just means
>>>> they
>>>> won’t be garbage-collected. The answer dictates if the use of critical
>>>> sections are actually necessary.
>>>>
>>>> Rule of thumb: don't try to bypass the JNI abstractions. It'll bite
>>> back hard if you switch between different JVM implementations.
>>>
>>> Specifically in HotSpot, jobject and friends are referred to as "JNI
>>> handles". That's because they're implemented as handles, i.e.
>>> double-indirection pointers. The underlying type that implements jobject is
>>> "oop*", where an "oop" is typedef'd from "oopDesc*", and oopDesc is the
>>> root type of garbage collected objects.
>>>
>>> jobject JNIHandle Java object
>>> [ oop* ] -> [ oop ] -> [ oopDesc ]
>>>
>>> Following the spec, what it guarantees is that for a given jobject, the
>>> JNIHandle representing the referent object will not be moved, but the
>>> actual referent object is free to move.
>>>
>>> Don't try to access raw oops from outside the VM by accessing the
>>> underlying handle internals. It's very likely to get dangling pointers
>>> after GCs.
>>>
>>> What are the effects for each garbage collector?
>>>>
>>>> All garbage collectors in HotSpot, except for CMS (mostly concurrent
>>> mark-sweep), are moving collectors. As such, after each completed
>>> collection, Java objects will move. If you're holding raw oops that the GC
>>> isn't aware of, they won't be updated during GC, and will be left dangling
>>> afterwards.
>>>
>>> JNI critical sections are implemented in HotSpot via temporarily
>>> disabling the GCs, and as soon as all JNI critical sections are completed,
>>> a full GC (by default) will be triggered to perform the delayed collection
>>> if needed. This mechanism is called the "GCLocker" in HotSpot.
>>> (GCs may be able to expand the heap to fulfill allocation requests, if
>>> the current capacity of the GC heap is smaller than the maximum capacity
>>> configured. So disabling the GC isn't always as bad as it sounds.)
>>>
>>>
>>>> I found an email on the archives that says that JNI critical sections do
>>>> not effect operation of CMS and I’m interested in understanding the
>>>> behavior for G1 as well.
>>>>
>>>> CMS is a mark-sweep collector, so it doesn't move objects. Thus, it is
>>> possible to perform CMS old gen collections even when the GCLocker is
>>> active (meaning there's at least one Java thread in a JNI critical section).
>>>
>>> G1, on the other hand, is a moving collector (an incremental copying
>>> collector with optional concurrent global marking; sometimes simply
>>> referred to as a concurrent mark-compact collector, but it's not the usual
>>> mark-compact notion). G1 GC cannot be performed when the GCLocker is active.
>>>
>>>
>>>> This all boils down to the ability to use unsafe raw direct pointers in
>>>> JNI
>>>> without critical sections in HotSpot.
>>>>
>>>> Nope. Don't try that on HotSpot.
>>> There are certain code patterns that might be able to access raw oops
>>> safely without going into JNI critical sections. But that involves very
>>> heavy VM internals knowledge, which is also subject to change without
>>> notice, so practically don't try that.
>>>
>>> Hope that helps,
>>> Kris (OpenJDK username: kmo)
>>>
>>>
>>>> Thank you,
>>>> J
>>>>
>>>
>>>
>>
>
More information about the hotspot-dev
mailing list