Reputation: 9540
The SetByteArrayRegion
function is implemented as
JNI_ENTRY(void, \
jni_Set##Result##ArrayRegion(JNIEnv *env, ElementType##Array array, jsize start, \
jsize len, const ElementType *buf)) \
JNIWrapper("Set" XSTR(Result) "ArrayRegion"); \
DTRACE_PROBE5(hotspot_jni, Set##Result##ArrayRegion__entry, env, array, start, len, buf);\
DT_VOID_RETURN_MARK(Set##Result##ArrayRegion); \
typeArrayOop dst = typeArrayOop(JNIHandles::resolve_non_null(array)); \
if (start < 0 || len < 0 || ((unsigned int)start + (unsigned int)len > (unsigned int)dst->length())) { \
THROW(vmSymbols::java_lang_ArrayIndexOutOfBoundsException()); \
} else { \
if (len > 0) { \
int sc = TypeArrayKlass::cast(dst->klass())->log2_element_size(); \
memcpy((u_char*) dst->Tag##_at_addr(start), \
(u_char*) buf, \
len << sc); \
} \
} \
JNI_END
As can be observed it calls memcpy
on a native pointer to the java heap array: dst->Tag##_at_addr(start)
. memcpy
itself is not atomic so I concluded that nothing stops GC from moving the array in the middle of memcpy
call.
Moreover the array can be moved somwhere right after the dst->Tag##_at_addr(start)
again causing memory corruption.
By contract, "critical" methods employ the GC_locker::lock_critical(thread);
.
So why are SetArrayRegion
methods safe? What did I miss?
Upvotes: 2
Views: 241
Reputation: 98495
As you can see, the function is wrapped in JNI_ENTRY
macro, which in turn performs ThreadInVMfromNative
state transition. This means, a Java thread executing SetByteArrayRegion
is guaranteed to be in _thread_in_vm
state.
Non-concurrent compacting collectors can move objects only at the global safepoint. A safepoint implies that all Java thread are either blocked or running native code (_thread_in_native
state).
So, if a safepoint is requested while SetByteArrayRegion
is running, JVM will wait until all threads complete current VM operation. Сontrariwise, if SetByteArrayRegion
is executed while GC is running, the thread will block at state transition until GC completes.
Upvotes: 4