diff -r 2bcd260edd04 src/share/vm/prims/jni.cpp
--- a/src/share/vm/prims/jni.cpp	Mon Aug 23 14:33:11 2021 +0100
+++ b/src/share/vm/prims/jni.cpp	Thu Oct 07 11:36:50 2021 +0200
@@ -36,10 +36,11 @@
 #include "jfr/support/jfrThreadId.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/ostream.hpp"
 #if INCLUDE_ALL_GCS
 #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp"
+#include "gc_implementation/shenandoah/shenandoahStringDedup.hpp"
 #endif // INCLUDE_ALL_GCS
 #include "memory/allocation.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/gcLocker.inline.hpp"
 #include "memory/oopFactory.hpp"
@@ -2628,11 +2629,11 @@
   jobject ret = JNIHandles::make_local(env, o->obj_field(offset));
 #if INCLUDE_ALL_GCS
   // If G1 is enabled and we are accessing the value of the referent
   // field in a reference object then we need to register a non-null
   // referent with the SATB barrier.
-  if (UseG1GC) {
+  if (UseG1GC || (UseShenandoahGC && ShenandoahSATBBarrier)) {
     bool needs_barrier = false;
 
     if (ret != NULL &&
         offset == java_lang_ref_Reference::referent_offset &&
         InstanceKlass::cast(k)->reference_type() != REF_NONE) {
@@ -4249,24 +4250,41 @@
       }
     }
   }
 JNI_END
 
+static oop lock_gc_or_pin_object(JavaThread* thread, jobject obj) {
+  if (Universe::heap()->supports_object_pinning()) {
+    const oop o = JNIHandles::resolve_non_null(obj);
+    return Universe::heap()->pin_object(thread, o);
+  } else {
+    GC_locker::lock_critical(thread);
+    return JNIHandles::resolve_non_null(obj);
+  }
+}
+
+static void unlock_gc_or_unpin_object(JavaThread* thread, jobject obj) {
+  if (Universe::heap()->supports_object_pinning()) {
+    const oop o = JNIHandles::resolve_non_null(obj);
+    return Universe::heap()->unpin_object(thread, o);
+  } else {
+    GC_locker::unlock_critical(thread);
+  }
+}
 
 JNI_ENTRY(void*, jni_GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy))
   JNIWrapper("GetPrimitiveArrayCritical");
 #ifndef USDT2
   DTRACE_PROBE3(hotspot_jni, GetPrimitiveArrayCritical__entry, env, array, isCopy);
 #else /* USDT2 */
  HOTSPOT_JNI_GETPRIMITIVEARRAYCRITICAL_ENTRY(
                                              env, array, (uintptr_t *) isCopy);
 #endif /* USDT2 */
-  GC_locker::lock_critical(thread);
   if (isCopy != NULL) {
     *isCopy = JNI_FALSE;
   }
-  oop a = JNIHandles::resolve_non_null(array);
+  oop a = lock_gc_or_pin_object(thread, array);
   assert(a->is_array(), "just checking");
   BasicType type;
   if (a->is_objArray()) {
     type = T_OBJECT;
   } else {
@@ -4290,11 +4308,11 @@
 #else /* USDT2 */
   HOTSPOT_JNI_RELEASEPRIMITIVEARRAYCRITICAL_ENTRY(
                                                   env, array, carray, mode);
 #endif /* USDT2 */
   // The array, carray and mode arguments are ignored
-  GC_locker::unlock_critical(thread);
+  unlock_gc_or_unpin_object(thread, array);
 #ifndef USDT2
   DTRACE_PROBE(hotspot_jni, ReleasePrimitiveArrayCritical__return);
 #else /* USDT2 */
 HOTSPOT_JNI_RELEASEPRIMITIVEARRAYCRITICAL_RETURN(
 );
@@ -4308,24 +4326,52 @@
   DTRACE_PROBE3(hotspot_jni, GetStringCritical__entry, env, string, isCopy);
 #else /* USDT2 */
   HOTSPOT_JNI_GETSTRINGCRITICAL_ENTRY(
                                       env, string, (uintptr_t *) isCopy);
 #endif /* USDT2 */
+  jchar* ret;
+  if (!UseShenandoahGC) {
   GC_locker::lock_critical(thread);
   if (isCopy != NULL) {
     *isCopy = JNI_FALSE;
   }
   oop s = JNIHandles::resolve_non_null(string);
   int s_len = java_lang_String::length(s);
   typeArrayOop s_value = java_lang_String::value(s);
   int s_offset = java_lang_String::offset(s);
-  const jchar* ret;
   if (s_len > 0) {
     ret = s_value->char_at_addr(s_offset);
   } else {
     ret = (jchar*) s_value->base(T_CHAR);
   }
+  }
+#if INCLUDE_ALL_GCS
+  else {
+    assert(UseShenandoahGC, "This path should only be taken with Shenandoah");
+    oop s = JNIHandles::resolve_non_null(string);
+    if (ShenandoahStringDedup::is_enabled()) {
+      typeArrayOop s_value = java_lang_String::value(s);
+      int s_len = java_lang_String::length(s);
+      ret = NEW_C_HEAP_ARRAY_RETURN_NULL(jchar, s_len + 1, mtInternal);  // add one for zero termination
+      /* JNI Specification states return NULL on OOM */
+      if (ret != NULL) {
+        memcpy(ret, s_value->char_at_addr(0), s_len * sizeof(jchar));
+        ret[s_len] = 0;
+      }
+      if (isCopy != NULL) *isCopy = JNI_TRUE;
+    } else {
+      typeArrayOop s_value = java_lang_String::value(s);
+      s_value = (typeArrayOop) Universe::heap()->pin_object(thread, s_value);
+      ret = (jchar *) s_value->base(T_CHAR);
+      if (isCopy != NULL) *isCopy = JNI_FALSE;
+    }
+  }
+#else
+  else {
+    ShouldNotReachHere();
+  }
+#endif
 #ifndef USDT2
   DTRACE_PROBE1(hotspot_jni, GetStringCritical__return, ret);
 #else /* USDT2 */
  HOTSPOT_JNI_GETSTRINGCRITICAL_RETURN(
                                       (uint16_t *) ret);
@@ -4340,12 +4386,32 @@
   DTRACE_PROBE3(hotspot_jni, ReleaseStringCritical__entry, env, str, chars);
 #else /* USDT2 */
   HOTSPOT_JNI_RELEASESTRINGCRITICAL_ENTRY(
                                           env, str, (uint16_t *) chars);
 #endif /* USDT2 */
+  if (!UseShenandoahGC) {
   // The str and chars arguments are ignored
   GC_locker::unlock_critical(thread);
+  }
+#if INCLUDE_ALL_GCS
+  else if (ShenandoahStringDedup::is_enabled()) {
+    assert(UseShenandoahGC, "This path should only be taken with Shenandoah");
+    // For copied string value, free jchar array allocated by earlier call to GetStringCritical.
+    // This assumes that ReleaseStringCritical bookends GetStringCritical.
+    FREE_C_HEAP_ARRAY(jchar, chars, mtInternal);
+  } else {
+    assert(UseShenandoahGC, "This path should only be taken with Shenandoah");
+    oop s = JNIHandles::resolve_non_null(str);
+    // For not copied string value, drop the associated gc-locker/pin.
+    typeArrayOop s_value = java_lang_String::value(s);
+    Universe::heap()->unpin_object(thread, s_value);
+  }
+#else
+  else {
+    ShouldNotReachHere();
+  }
+#endif
 #ifndef USDT2
   DTRACE_PROBE(hotspot_jni, ReleaseStringCritical__return);
 #else /* USDT2 */
 HOTSPOT_JNI_RELEASESTRINGCRITICAL_RETURN(
 );