java - 为什么 SetByteArrayRegion 不会损坏内存?

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

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 堆数组的 native 指针上调用 memcpy:dst->Tag##_at_addr(start)memcpy 本身不是原子的,因此我得出的结论是,没有什么可以阻止 GC 在 memcpy 调用过程中移动数组。

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

根据契约(Contract),“关键”方法采用GC_locker::lock_ritic(thread);

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

最佳答案

如您所见,该函数被包装在 JNI_ENTRY 中宏,依次执行 ThreadInVMfromNative 状态转换。这意味着,一个 Java 线程正在执行 SetByteArrayRegion保证位于 _thread_in_vm状态。

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

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

关于java - 为什么 SetByteArrayRegion 不会损坏内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56396045/

相关文章:

java - 您知道有哪些使用 Java Web 服务的开源项目吗?

java - Java中如何有效清除部分Graphics层?

java - 方法引用 lambda 中的显式类型提示导致原始类型

c# - C# 和 Java 中的垃圾回收之间的根本区别是什么?

java - Linux 上的 FileChannel.write 会产生大量垃圾,但在 Mac 上不会

java - Gradle:如何在单独的进程/JVM 上运行 Sonar

java - 如何从批处理程序中检测java安装

c# - 我们应该使用 Scala 而不是 Java 吗?

java - 如果文件大小大于 X mb,则停止记录

C# - 释放对象使用的所有资源