Clarification on what the JNI spec means by nonmovable
David Holmes
david.holmes at oracle.com
Thu Sep 8 22:11:28 UTC 2016
On 9/09/2016 7:21 AM, Jon V. wrote:
> 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.
"throwing an exception" here means to establish a pending exception that
will be thrown when you return to Java code. Unless you code it into the
if-block there is no transfer of control that will skip the Release*
calls. The example is still not great though - not sure what happens if
you try to release something you didn't acquire.
David
> 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