Почему SetByteArrayRegion не портит память? - PullRequest
2 голосов
/ 31 мая 2019

Функция 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

Как можно заметить, он вызывает memcpy для собственного указателя на массив кучи Java: dst->Tag##_at_addr(start). memcpy само по себе не является атомарным, поэтому я пришел к выводу, что ничто не мешает GC перемещать массив в середине вызова memcpy.

Кроме того, массив можно переместить куда-то сразу после dst->Tag##_at_addr(start), что снова приведет к повреждению памяти.

По контракту "критические" методы используют GC_locker::lock_critical(thread);.

Так почему же SetArrayRegion методы безопасны? Что я пропустил?

1 Ответ

4 голосов
/ 31 мая 2019

Как видите, функция заключена в макрос JNI_ENTRY, который, в свою очередь, выполняет переход состояния ThreadInVMfromNative. Это означает, что поток Java, выполняющий SetByteArrayRegion, гарантированно находится в состоянии _thread_in_vm.

Непараллельные уплотняющие коллекторы могут перемещать объекты только в глобальной безопасной точке. Безопасная точка подразумевает, что все потоки Java либо заблокированы, либо выполняют собственный код (состояние _thread_in_native).

Итак, если безопасная точка запрашивается во время работы SetByteArrayRegion, JVM будет ждать, пока все потоки не завершат текущую работу виртуальной машины. И наоборот, если SetByteArrayRegion выполняется во время работы GC, поток будет блокироваться при переходе состояния до завершения GC.

...