为什么 SetByteArrayRegion 不会破坏内存?
Why does not SetByteArrayRegion corrupt memory?
SetByteArrayRegion
函数实现为
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
可以观察到它在指向 java 堆数组的本机指针上调用 memcpy
:dst->Tag##_at_addr(start)
。 memcpy
本身不是原子的,所以我得出结论,没有什么能阻止 GC 在 memcpy
调用中间移动数组。
此外,数组可以在 dst->Tag##_at_addr(start)
之后的某处移动,再次导致内存损坏。
根据约定,"critical" 方法使用 GC_locker::lock_critical(thread);
。
那么为什么 SetArrayRegion
方法是安全的?我错过了什么?
如您所见,函数包装在 JNI_ENTRY
macro, which in turn performs ThreadInVMfromNative
状态转换中。这意味着,执行 SetByteArrayRegion
的 Java 线程保证处于 _thread_in_vm
状态。
Non-concurrent 压缩收集器只能在全局安全点移动对象。安全点意味着所有 Java 线程都被阻塞或 运行 本机代码(_thread_in_native
状态)。
因此,如果在 SetByteArrayRegion
为 运行 时请求安全点,JVM 将等待所有线程完成当前 VM 操作。反之,如果SetByteArrayRegion
在GC运行时执行,线程会在状态转换时阻塞,直到GC完成
SetByteArrayRegion
函数实现为
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
可以观察到它在指向 java 堆数组的本机指针上调用 memcpy
:dst->Tag##_at_addr(start)
。 memcpy
本身不是原子的,所以我得出结论,没有什么能阻止 GC 在 memcpy
调用中间移动数组。
此外,数组可以在 dst->Tag##_at_addr(start)
之后的某处移动,再次导致内存损坏。
根据约定,"critical" 方法使用 GC_locker::lock_critical(thread);
。
那么为什么 SetArrayRegion
方法是安全的?我错过了什么?
如您所见,函数包装在 JNI_ENTRY
macro, which in turn performs ThreadInVMfromNative
状态转换中。这意味着,执行 SetByteArrayRegion
的 Java 线程保证处于 _thread_in_vm
状态。
Non-concurrent 压缩收集器只能在全局安全点移动对象。安全点意味着所有 Java 线程都被阻塞或 运行 本机代码(_thread_in_native
状态)。
因此,如果在 SetByteArrayRegion
为 运行 时请求安全点,JVM 将等待所有线程完成当前 VM 操作。反之,如果SetByteArrayRegion
在GC运行时执行,线程会在状态转换时阻塞,直到GC完成