【问题标题】:Why does not SetByteArrayRegion corrupt memory?为什么 SetByteArrayRegion 不会损坏内存?
【发布时间】:2019-05-31 13:29:26
【问题描述】:

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 堆数组的本机指针上调用 memcpydst-&gt;Tag##_at_addr(start)memcpy 本身不是原子的,所以我得出结论,没有什么能阻止 GC 在 memcpy 调用的中间移动数组。

此外,可以将数组移动到 dst-&gt;Tag##_at_addr(start) 之后的某个位置,再次导致内存损坏。

根据合同,“关键”方法使用GC_locker::lock_critical(thread);

那么为什么SetArrayRegion 方法是安全的?我错过了什么?

【问题讨论】:

  • 这取决于实现。它可以在复制数组时禁用 GC,也可以将数组标记为不可移动。
  • @MauricePerry 你能详细说明一下吗? SetArrayRegion 是在哪里完成的?我查看了JNIHandles::resolve_non_null,但它只是简单地转换为oop*,并用一些断言取消引用指针。

标签: java garbage-collection jvm java-native-interface jvm-hotspot


【解决方案1】:

如您所见,该函数封装在JNI_ENTRY 宏中,该宏又执行ThreadInVMfromNative 状态转换。这意味着,执行SetByteArrayRegion 的Java 线程保证处于_thread_in_vm 状态。

非并发压缩收集器只能在全局安全点移动对象。安全点意味着所有 Java 线程要么被阻塞,要么正在运行本机代码(_thread_in_native 状态)。

因此,如果在 SetByteArrayRegion 运行时请求安全点,JVM 将等待所有线程完成当前 VM 操作。 Сontrariwise,如果在 GC 运行时执行SetByteArrayRegion,线程将在状态转换时阻塞,直到 GC 完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 2019-10-23
    • 1970-01-01
    • 1970-01-01
    • 2019-10-13
    • 2013-09-25
    • 1970-01-01
    相关资源
    最近更新 更多