diff a/make/autoconf/hotspot.m4 b/make/autoconf/hotspot.m4
--- a/make/autoconf/hotspot.m4
+++ b/make/autoconf/hotspot.m4
@@ -24,11 +24,11 @@
 #
 
 # All valid JVM features, regardless of platform
 VALID_JVM_FEATURES="compiler1 compiler2 zero minimal dtrace jvmti jvmci \
     graal vm-structs jni-check services management epsilongc g1gc parallelgc serialgc shenandoahgc zgc nmt cds \
-    static-build link-time-opt aot jfr"
+    static-build link-time-opt aot jfr tsan"
 
 # Deprecated JVM features (these are ignored, but with a warning)
 DEPRECATED_JVM_FEATURES="trace cmsgc"
 
 # All valid JVM variants
@@ -547,10 +547,51 @@
     else
       AC_MSG_RESULT([no])
     fi
   fi
 
+  AC_MSG_CHECKING([if tsan should be built])
+  # Check if user explicitly disabled tsan
+  if HOTSPOT_IS_JVM_FEATURE_DISABLED(tsan); then
+    AC_MSG_RESULT([no, forced])
+    INCLUDE_TSAN="false"
+  # Only enable ThreadSanitizer on supported platforms
+  elif test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = "xx86_64"; then
+    AC_MSG_RESULT([yes])
+    NON_MINIMAL_FEATURES="$NON_MINIMAL_FEATURES tsan"
+    INCLUDE_TSAN="true"
+  else
+    AC_MSG_RESULT([no, platform not supported])
+    INCLUDE_TSAN="false"
+    if HOTSPOT_CHECK_JVM_FEATURE(tsan); then
+      AC_MSG_ERROR([ThreadSanitizer is currently not supported on this platform.])
+    fi
+  fi
+
+  # Add a configure option --<enable|disable>-tsan-launcher to allow
+  # more control on whether to link TSAN runtime with the launcher.
+  AC_ARG_ENABLE([tsan-launcher], [AS_HELP_STRING([--enable-tsan-launcher],
+      [link tsan runtime with the default JDK launcher. Default is consistent with whether tsan feature is enabled.])])
+  AC_MSG_CHECKING([if tsan should be linked with JDK launcher])
+  if test "x$INCLUDE_TSAN" = "xtrue"; then
+    if test "x$enable_tsan_launcher" = "xyes"; then
+      AC_MSG_RESULT([yes, forced])
+    elif test "x$enable_tsan_launcher" = "xno"; then
+      AC_MSG_RESULT([no, forced])
+      INCLUDE_TSAN="false"
+    else
+      AC_MSG_RESULT([yes, default])
+    fi
+  else
+    AC_MSG_RESULT([no, tsan feature is disabled])
+    if test "x$enable_tsan_launcher" = "xyes"; then
+      AC_MSG_ERROR([--enable-tsan-launcher can only be used when tsan feature is enabled.])
+    fi
+  fi
+
+  AC_SUBST(INCLUDE_TSAN)
+
   # Enable features depending on variant.
   JVM_FEATURES_server="compiler1 compiler2 $NON_MINIMAL_FEATURES $JVM_FEATURES $JVM_FEATURES_jvmci $JVM_FEATURES_aot $JVM_FEATURES_graal"
   JVM_FEATURES_client="compiler1 $NON_MINIMAL_FEATURES $JVM_FEATURES"
   JVM_FEATURES_core="$NON_MINIMAL_FEATURES $JVM_FEATURES"
   JVM_FEATURES_minimal="compiler1 minimal serialgc $JVM_FEATURES $JVM_FEATURES_link_time_opt"
diff a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in
--- a/make/autoconf/spec.gmk.in
+++ b/make/autoconf/spec.gmk.in
@@ -865,10 +865,11 @@
 #
 
 INCLUDE_SA=@INCLUDE_SA@
 INCLUDE_GRAAL=@INCLUDE_GRAAL@
 INCLUDE_JVMCI=@INCLUDE_JVMCI@
+INCLUDE_TSAN:=@INCLUDE_TSAN@
 
 OS_VERSION_MAJOR:=@OS_VERSION_MAJOR@
 OS_VERSION_MINOR:=@OS_VERSION_MINOR@
 OS_VERSION_MICRO:=@OS_VERSION_MICRO@
 
diff a/make/hotspot/lib/JvmFeatures.gmk b/make/hotspot/lib/JvmFeatures.gmk
--- a/make/hotspot/lib/JvmFeatures.gmk
+++ b/make/hotspot/lib/JvmFeatures.gmk
@@ -173,10 +173,15 @@
 ifneq ($(call check-jvm-feature, jfr), true)
   JVM_CFLAGS_FEATURES += -DINCLUDE_JFR=0
   JVM_EXCLUDE_PATTERNS += jfr
 endif
 
+ifneq ($(call check-jvm-feature, tsan), true)
+  JVM_CFLAGS_FEATURES += -DINCLUDE_TSAN=0
+  JVM_EXCLUDE_PATTERNS += tsan
+endif
+
 ################################################################################
 
 ifeq ($(call check-jvm-feature, link-time-opt), true)
   # NOTE: Disable automatic opimization level and let the explicit cflag control
   # optimization level instead. This activates O3 on slowdebug builds, just
diff a/make/hotspot/symbols/symbols-shared b/make/hotspot/symbols/symbols-shared
--- a/make/hotspot/symbols/symbols-shared
+++ b/make/hotspot/symbols/symbols-shared
@@ -29,6 +29,7 @@
 jio_vsnprintf
 JNI_CreateJavaVM
 JNI_GetCreatedJavaVMs
 JNI_GetDefaultJavaVMInitArgs
 JVM_FindClassFromBootLoader
+JVM_InitAgentProperties
 JVM_InitAgentProperties
diff a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix
--- a/make/hotspot/symbols/symbols-unix
+++ b/make/hotspot/symbols/symbols-unix
@@ -126,10 +126,11 @@
 JVM_GetSimpleBinaryName
 JVM_GetStackAccessControlContext
 JVM_GetSystemPackage
 JVM_GetSystemPackages
 JVM_GetTemporaryDirectory
+JVM_GetTsanEnabled
 JVM_GetVmArguments
 JVM_Halt
 JVM_HasReferencePendingList
 JVM_HoldsLock
 JVM_IHashCode
diff a/make/launcher/Launcher-java.base.gmk b/make/launcher/Launcher-java.base.gmk
--- a/make/launcher/Launcher-java.base.gmk
+++ b/make/launcher/Launcher-java.base.gmk
@@ -41,10 +41,11 @@
     CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS -DENABLE_ARG_FILES, \
     EXTRA_RC_FLAGS := $(JAVA_RC_FLAGS), \
     VERSION_INFO_RESOURCE := $(JAVA_VERSION_INFO_RESOURCE), \
     OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/java_objs, \
     OPTIMIZATION := HIGH, \
+    INCLUDE_TSAN := $(INCLUDE_TSAN), \
 ))
 
 $(SUPPORT_OUTPUTDIR)/modules_cmds/java.base/java$(EXE_SUFFIX): $(BUILD_LAUNCHER_java)
 	$(call MakeTargetDir)
 	$(RM) $@
diff a/make/launcher/LauncherCommon.gmk b/make/launcher/LauncherCommon.gmk
--- a/make/launcher/LauncherCommon.gmk
+++ b/make/launcher/LauncherCommon.gmk
@@ -76,10 +76,11 @@
 # EXTRA_RC_FLAGS   Additional EXTRA_RC_FLAGS
 # MACOSX_PRIVILEGED   On macosx, allow to access other processes
 # OPTIMIZATION   Override default optimization level (LOW)
 # OUTPUT_DIR   Override default output directory
 # VERSION_INFO_RESOURCE   Override default Windows resource file
+# INCLUDE_TSAN   If true, pass compiler and linker flags for TSAN.
 SetupBuildLauncher = $(NamedParamsMacroTemplate)
 define SetupBuildLauncherBody
   # Setup default values (unless overridden)
   ifeq ($$($1_OPTIMIZATION), )
     $1_OPTIMIZATION := LOW
@@ -129,10 +130,21 @@
 
   ifeq ($(USE_EXTERNAL_LIBZ), true)
     $1_LIBS += -lz
   endif
 
+  ifeq ($$($1_INCLUDE_TSAN), true)
+    $1_CFLAGS += -DINCLUDE_TSAN
+    # TSAN runtime needs to be statically or dynamically linked with the launcher
+    # instead of libjvm.so, because initialization of TSAN runtime has to happen
+    # early at program start.
+    # '-fsanitize=thread' works as a link-only flag for either GCC or Clang.
+    # With GCC, it dynamically links with libtsan.so; with Clang, it statically
+    # links the runtime into the launcher's executable.
+    $1_LDFLAGS += -fsanitize=thread
+  endif
+
   $1_WINDOWS_JLI_LIB := $(call FindStaticLib, java.base, jli, /libjli)
 
   $$(eval $$(call SetupJdkExecutable, BUILD_LAUNCHER_$1, \
       NAME := $1, \
       EXTRA_FILES := $(LAUNCHER_SRC)/main.c, \
diff a/make/lib/CoreLibraries.gmk b/make/lib/CoreLibraries.gmk
--- a/make/lib/CoreLibraries.gmk
+++ b/make/lib/CoreLibraries.gmk
@@ -189,10 +189,14 @@
   endif
 endif
 
 LIBJLI_CFLAGS += $(LIBZ_CFLAGS)
 
+ifeq ($(INCLUDE_TSAN), true)
+  LIBJLI_CFLAGS += -DINCLUDE_TSAN
+endif
+
 ifneq ($(USE_EXTERNAL_LIBZ), true)
   LIBJLI_EXTRA_FILES += \
       $(addprefix $(TOPDIR)/src/java.base/share/native/libzip/zlib/, \
           inflate.c \
           inftrees.c \
diff a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk
--- a/make/test/JtregNativeHotspot.gmk
+++ b/make/test/JtregNativeHotspot.gmk
@@ -864,10 +864,17 @@
     BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exestack-gap := -ljvm -lpthread
     BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exestack-tls := -ljvm
     BUILD_TEST_exeinvoke_exeinvoke.c_OPTIMIZATION := NONE
     BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl
+
+    BUILD_HOTSPOT_JTREG_LIBRARIES_CFLAGS_libAbstractNativeLoop := -fsanitize=thread
+    ifeq ($(TOOLCHAIN_TYPE), gcc)
+      # Ignore unresolved symbols from TSAN's runtime.
+      # The symbols will be available at runtime as TSAN runtime is linked with the launcher.
+      BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libAbstractNativeLoop := -Wl,--unresolved-symbols=ignore-in-object-files
+    endif
 else
   BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c libTestJNI.c \
       exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp
 endif
 
diff a/src/hotspot/cpu/x86/interp_masm_x86.cpp b/src/hotspot/cpu/x86/interp_masm_x86.cpp
--- a/src/hotspot/cpu/x86/interp_masm_x86.cpp
+++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp
@@ -1172,10 +1172,11 @@
 //      rax, rbx
 void InterpreterMacroAssembler::lock_object(Register lock_reg) {
   assert(lock_reg == LP64_ONLY(c_rarg1) NOT_LP64(rdx),
          "The argument is only for looks. It must be c_rarg1");
 
+  TSAN_RUNTIME_ONLY(push_ptr(lock_reg));
   if (UseHeavyMonitors) {
     call_VM(noreg,
             CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
             lock_reg);
   } else {
@@ -1250,10 +1251,19 @@
             CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
             lock_reg);
 
     bind(done);
   }
+
+  TSAN_RUNTIME_ONLY(
+    pop_ptr(lock_reg);
+    pusha();
+    call_VM(noreg,
+            CAST_FROM_FN_PTR(address, SharedRuntime::tsan_interp_lock),
+            lock_reg);
+    popa();
+  );
 }
 
 
 // Unlocks an object. Used in monitorexit bytecode and
 // remove_activation.  Throws an IllegalMonitorException if object is
@@ -1269,10 +1279,18 @@
 // rax, rbx, rcx, rdx
 void InterpreterMacroAssembler::unlock_object(Register lock_reg) {
   assert(lock_reg == LP64_ONLY(c_rarg1) NOT_LP64(rdx),
          "The argument is only for looks. It must be c_rarg1");
 
+  TSAN_RUNTIME_ONLY(
+    pusha();
+    call_VM(noreg,
+            CAST_FROM_FN_PTR(address, SharedRuntime::tsan_interp_unlock),
+            lock_reg);
+    popa();
+  );
+
   if (UseHeavyMonitors) {
     call_VM(noreg,
             CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit),
             lock_reg);
   } else {
@@ -2013,10 +2031,14 @@
     get_method(rarg);
     call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry),
                  rthread, rarg);
   }
 
+  TSAN_RUNTIME_ONLY(call_VM(noreg,
+                            CAST_FROM_FN_PTR(address,
+                                             SharedRuntime::tsan_interp_method_entry)));
+
   // RedefineClasses() tracing support for obsolete method entry
   if (log_is_enabled(Trace, redefine, class, obsolete)) {
     NOT_LP64(get_thread(rthread);)
     get_method(rarg);
     call_VM_leaf(
@@ -2050,10 +2072,17 @@
             CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit));
     bind(L);
     pop(state);
   }
 
+  TSAN_RUNTIME_ONLY(
+    push(state);
+    call_VM_leaf(CAST_FROM_FN_PTR(address,
+                                  SharedRuntime::tsan_interp_method_exit));
+    pop(state);
+  );
+
   {
     SkipIfEqual skip(this, &DTraceMethodProbes, false);
     push(state);
     NOT_LP64(get_thread(rthread);)
     get_method(rarg);
diff a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
@@ -2440,10 +2440,19 @@
       CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry),
       r15_thread, c_rarg1);
     restore_args(masm, total_c_args, c_arg, out_regs);
   }
 
+  TSAN_RUNTIME_ONLY(
+    // protect the args we've loaded
+    save_args(masm, total_c_args, c_arg, out_regs);
+    __ call_VM(noreg,
+      CAST_FROM_FN_PTR(address, SharedRuntime::tsan_interp_method_entry),
+      r15_thread);
+    restore_args(masm, total_c_args, c_arg, out_regs);
+  );
+
   // RedefineClasses() tracing support for obsolete method entry
   if (log_is_enabled(Trace, redefine, class, obsolete)) {
     // protect the args we've loaded
     save_args(masm, total_c_args, c_arg, out_regs);
     __ mov_metadata(c_rarg1, method());
@@ -2519,10 +2528,18 @@
     __ jcc(Assembler::notEqual, slow_path_lock);
 
     // Slow path will re-enter here
 
     __ bind(lock_done);
+
+    TSAN_RUNTIME_ONLY(
+      __ pusha();
+      __ call_VM(noreg,
+                 CAST_FROM_FN_PTR(address, SharedRuntime::tsan_oop_lock),
+                 obj_reg);
+      __ popa();
+    );
   }
 
 
   // Finally just about ready to make the JNI call
 
@@ -2654,10 +2671,18 @@
 
     // Get locked oop from the handle we passed to jni
     __ movptr(obj_reg, Address(oop_handle_reg, 0));
     __ resolve(IS_NOT_NULL, obj_reg);
 
+    TSAN_RUNTIME_ONLY(
+      __ pusha();
+      __ call_VM(noreg, CAST_FROM_FN_PTR(address,
+                                         SharedRuntime::tsan_oop_unlock),
+                 obj_reg);
+      __ popa();
+    );
+
     Label done;
 
     if (UseBiasedLocking) {
       __ biased_locking_exit(obj_reg, old_hdr, done);
     }
@@ -2690,10 +2715,18 @@
     }
 
     __ bind(done);
 
   }
+
+  TSAN_RUNTIME_ONLY(
+    save_native_result(masm, ret_type, stack_slots);
+    __ call_VM_leaf(
+         CAST_FROM_FN_PTR(address, SharedRuntime::tsan_interp_method_exit));
+    restore_native_result(masm, ret_type, stack_slots);
+  );
+
   {
     SkipIfEqual skip(masm, &DTraceMethodProbes, false);
     save_native_result(masm, ret_type, stack_slots);
     __ mov_metadata(c_rarg1, method());
     __ call_VM_leaf(
diff a/src/hotspot/cpu/x86/templateTable_x86.cpp b/src/hotspot/cpu/x86/templateTable_x86.cpp
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp
@@ -765,91 +765,209 @@
   __ mov(NOT_LP64(rax) LP64_ONLY(c_rarg1), array);
   __ jump(ExternalAddress(Interpreter::_throw_ArrayIndexOutOfBoundsException_entry));
   __ bind(skip);
 }
 
+#if INCLUDE_TSAN
+
+TemplateTable::TsanMemoryReleaseAcquireFunction TemplateTable::tsan_release_acquire_method(
+    TsanMemoryReadWriteFunction tsan_function) {
+  if (tsan_function == SharedRuntime::tsan_read1
+      || tsan_function == SharedRuntime::tsan_read2
+      || tsan_function == SharedRuntime::tsan_read4
+      || tsan_function == SharedRuntime::tsan_read8) {
+    return SharedRuntime::tsan_acquire;
+  } else if (tsan_function == SharedRuntime::tsan_write1
+      || tsan_function == SharedRuntime::tsan_write2
+      || tsan_function == SharedRuntime::tsan_write4
+      || tsan_function == SharedRuntime::tsan_write8) {
+    return SharedRuntime::tsan_release;
+  }
+  ShouldNotReachHere();
+  return NULL;
+}
+
+void TemplateTable::tsan_observe_get_or_put(
+    const Address &field,
+    Register flags,
+    TsanMemoryReadWriteFunction tsan_function,
+    TosState tos) {
+  assert(flags == rdx, "flags should be in rdx register");
+  assert(ThreadSanitizer, "ThreadSanitizer should be set");
+
+  TsanMemoryReleaseAcquireFunction releaseAcquireFunction =
+      tsan_release_acquire_method(tsan_function);
+
+  Label done, notAcquireRelease;
+
+  // We could save some instructions by only saving the registers we need.
+  __ pusha();
+  // pusha() doesn't save xmm0, which tsan_function clobbers and the
+  // interpreter still needs.
+  // This really only needs to be done for some of the float/double accesses,
+  // but it's here because it's cleaner.
+  __ push_d(xmm0);
+  DEBUG_ONLY(
+    __ pusha();
+    __ movptr(c_rarg0, field.base());
+    __ leaq(c_rarg1, field);
+    __ subq(c_rarg1, field.base());
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::verify_oop_index),
+                    c_rarg0 /* oop */, c_rarg1 /* index */);
+    __ popa();
+  );
+  // For volatile reads/writes use an acquire/release.
+  // If a reference is annotated to be ignored, assume it's safe to
+  // access the object it's referring to and create a happens-before relation
+  // between the accesses to this reference.
+  int32_t acquire_release_mask = 1 << ConstantPoolCacheEntry::is_volatile_shift |
+      ((tos == atos) ? 1 << ConstantPoolCacheEntry::is_tsan_ignore_shift : 0);
+  __ testl(flags, acquire_release_mask);
+  __ jcc(Assembler::zero, notAcquireRelease);
+
+  __ leaq(c_rarg0, field);
+  __ call_VM_leaf(CAST_FROM_FN_PTR(address, releaseAcquireFunction), c_rarg0);
+  if (ThreadSanitizerJavaMemory) {
+    __ jmp(done);
+
+    __ bind(notAcquireRelease);
+    // Ignore reads/writes to final fields. They can't be racy.
+    int32_t ignore_mask = 1 << ConstantPoolCacheEntry::is_final_shift |
+        1 << ConstantPoolCacheEntry::is_tsan_ignore_shift;
+    __ testl(flags, ignore_mask);
+    __ jcc(Assembler::notZero, done);
+
+    __ leaq(c_rarg0, field);
+    __ get_method(c_rarg1);
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, tsan_function),
+                    c_rarg0 /* addr */, c_rarg1 /* method */, rbcp /* bcp */);
+
+    __ bind(done);
+  } else {
+    __ bind(notAcquireRelease);
+  }
+  __ pop_d(xmm0);
+  __ popa();
+}
+
+void TemplateTable::tsan_observe_load_or_store(
+    const Address& field, TsanMemoryReadWriteFunction tsan_function) {
+  assert(ThreadSanitizer, "ThreadSanitizer should be set");
+  if (!ThreadSanitizerJavaMemory) {
+    return;
+  }
+  // We could save some instructions by only saving the registers we need.
+  __ pusha();
+  // pusha() doesn't save xmm0, which tsan_function clobbers and the
+  // interpreter still needs.
+  // This really only needs to be done for some of the float/double accesses,
+  // but it's here because it's cleaner.
+  __ push_d(xmm0);
+  DEBUG_ONLY(
+    __ pusha();
+    __ movptr(c_rarg0, field.base());
+    __ leaq(c_rarg1, field);
+    __ subq(c_rarg1, field.base());
+    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::verify_oop_index),
+                    c_rarg0 /* oop */, c_rarg1 /* index */);
+    __ popa();
+  );
+  __ leaq(c_rarg0, field);
+  __ get_method(c_rarg1);
+  __ call_VM_leaf(CAST_FROM_FN_PTR(address, tsan_function),
+                  c_rarg0 /* addr */, c_rarg1 /* method */, rbcp /* bcp */);
+  __ pop_d(xmm0);
+  __ popa();
+}
+
+#endif  // INCLUDE_TSAN
+
 void TemplateTable::iaload() {
   transition(itos, itos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  __ access_load_at(T_INT, IN_HEAP | IS_ARRAY, rax,
-                    Address(rdx, rax, Address::times_4,
-                            arrayOopDesc::base_offset_in_bytes(T_INT)),
-                    noreg, noreg);
+  Address addr(rdx, rax, Address::times_4,
+               arrayOopDesc::base_offset_in_bytes(T_INT));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read4));
+  __ access_load_at(T_INT, IN_HEAP | IS_ARRAY, rax, addr, noreg, noreg);
 }
 
 void TemplateTable::laload() {
   transition(itos, ltos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
   NOT_LP64(__ mov(rbx, rax));
   // rbx,: index
-  __ access_load_at(T_LONG, IN_HEAP | IS_ARRAY, noreg /* ltos */,
-                    Address(rdx, rbx, Address::times_8,
-                            arrayOopDesc::base_offset_in_bytes(T_LONG)),
-                    noreg, noreg);
+  Address addr(rdx, rbx, Address::times_8,
+               arrayOopDesc::base_offset_in_bytes(T_LONG));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read8));
+  __ access_load_at(T_LONG, IN_HEAP | IS_ARRAY, noreg /* ltos */, addr, noreg,
+                    noreg);
 }
 
 
 
 void TemplateTable::faload() {
   transition(itos, ftos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  __ access_load_at(T_FLOAT, IN_HEAP | IS_ARRAY, noreg /* ftos */,
-                    Address(rdx, rax,
-                            Address::times_4,
-                            arrayOopDesc::base_offset_in_bytes(T_FLOAT)),
-                    noreg, noreg);
+  Address addr(rdx, rax, Address::times_4,
+               arrayOopDesc::base_offset_in_bytes(T_FLOAT));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read4));
+  __ access_load_at(T_FLOAT, IN_HEAP | IS_ARRAY, noreg /* ftos */, addr, noreg,
+                    noreg);
 }
 
 void TemplateTable::daload() {
   transition(itos, dtos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  __ access_load_at(T_DOUBLE, IN_HEAP | IS_ARRAY, noreg /* dtos */,
-                    Address(rdx, rax,
-                            Address::times_8,
-                            arrayOopDesc::base_offset_in_bytes(T_DOUBLE)),
-                    noreg, noreg);
+  Address addr(rdx, rax, Address::times_8,
+               arrayOopDesc::base_offset_in_bytes(T_DOUBLE));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read8));
+  __ access_load_at(T_DOUBLE, IN_HEAP | IS_ARRAY, noreg /* dtos */, addr, noreg,
+                    noreg);
 }
 
 void TemplateTable::aaload() {
   transition(itos, atos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  do_oop_load(_masm,
-              Address(rdx, rax,
-                      UseCompressedOops ? Address::times_4 : Address::times_ptr,
-                      arrayOopDesc::base_offset_in_bytes(T_OBJECT)),
-              rax,
-              IS_ARRAY);
+  Address addr(rdx, rax,
+               UseCompressedOops ? Address::times_4 : Address::times_ptr,
+               arrayOopDesc::base_offset_in_bytes(T_OBJECT));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(
+      addr, UseCompressedOops ? SharedRuntime::tsan_read4
+                              : SharedRuntime::tsan_read8));
+  do_oop_load(_masm, addr, rax, IS_ARRAY);
 }
 
 void TemplateTable::baload() {
   transition(itos, itos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  __ access_load_at(T_BYTE, IN_HEAP | IS_ARRAY, rax,
-                    Address(rdx, rax, Address::times_1, arrayOopDesc::base_offset_in_bytes(T_BYTE)),
-                    noreg, noreg);
+  Address addr(rdx, rax, Address::times_1,
+               arrayOopDesc::base_offset_in_bytes(T_BYTE));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read1));
+  __ access_load_at(T_BYTE, IN_HEAP | IS_ARRAY, rax, addr, noreg, noreg);
 }
 
 void TemplateTable::caload() {
   transition(itos, itos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  __ access_load_at(T_CHAR, IN_HEAP | IS_ARRAY, rax,
-                    Address(rdx, rax, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_CHAR)),
-                    noreg, noreg);
+  Address addr(rdx, rax, Address::times_2,
+               arrayOopDesc::base_offset_in_bytes(T_CHAR));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read2));
+  __ access_load_at(T_CHAR, IN_HEAP | IS_ARRAY, rax, addr, noreg, noreg);
 }
 
 // iload followed by caload frequent pair
 void TemplateTable::fast_icaload() {
   transition(vtos, itos);
@@ -869,13 +987,14 @@
 void TemplateTable::saload() {
   transition(itos, itos);
   // rax: index
   // rdx: array
   index_check(rdx, rax); // kills rbx
-  __ access_load_at(T_SHORT, IN_HEAP | IS_ARRAY, rax,
-                    Address(rdx, rax, Address::times_2, arrayOopDesc::base_offset_in_bytes(T_SHORT)),
-                    noreg, noreg);
+  Address addr(rdx, rax, Address::times_2,
+               arrayOopDesc::base_offset_in_bytes(T_SHORT));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_read2));
+  __ access_load_at(T_SHORT, IN_HEAP | IS_ARRAY, rax, addr, noreg, noreg);
 }
 
 void TemplateTable::iload(int n) {
   transition(vtos, itos);
   __ movl(rax, iaddress(n));
@@ -1063,55 +1182,58 @@
   __ pop_i(rbx);
   // rax: value
   // rbx: index
   // rdx: array
   index_check(rdx, rbx); // prefer index in rbx
-  __ access_store_at(T_INT, IN_HEAP | IS_ARRAY,
-                     Address(rdx, rbx, Address::times_4,
-                             arrayOopDesc::base_offset_in_bytes(T_INT)),
-                     rax, noreg, noreg);
+  Address addr(rdx, rbx, Address::times_4,
+               arrayOopDesc::base_offset_in_bytes(T_INT));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_write4));
+  __ access_store_at(T_INT, IN_HEAP | IS_ARRAY, addr, rax, noreg, noreg);
 }
 
 void TemplateTable::lastore() {
   transition(ltos, vtos);
   __ pop_i(rbx);
   // rax,: low(value)
   // rcx: array
   // rdx: high(value)
   index_check(rcx, rbx);  // prefer index in rbx,
   // rbx,: index
-  __ access_store_at(T_LONG, IN_HEAP | IS_ARRAY,
-                     Address(rcx, rbx, Address::times_8,
-                             arrayOopDesc::base_offset_in_bytes(T_LONG)),
-                     noreg /* ltos */, noreg, noreg);
+  Address addr(rcx, rbx, Address::times_8,
+               arrayOopDesc::base_offset_in_bytes(T_LONG));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_write8));
+  __ access_store_at(T_LONG, IN_HEAP | IS_ARRAY, addr, noreg /* ltos */, noreg,
+                     noreg);
 }
 
 
 void TemplateTable::fastore() {
   transition(ftos, vtos);
   __ pop_i(rbx);
   // value is in UseSSE >= 1 ? xmm0 : ST(0)
   // rbx:  index
   // rdx:  array
   index_check(rdx, rbx); // prefer index in rbx
-  __ access_store_at(T_FLOAT, IN_HEAP | IS_ARRAY,
-                     Address(rdx, rbx, Address::times_4,
-                             arrayOopDesc::base_offset_in_bytes(T_FLOAT)),
-                     noreg /* ftos */, noreg, noreg);
+  Address addr(rdx, rbx, Address::times_4,
+               arrayOopDesc::base_offset_in_bytes(T_FLOAT));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_write4));
+  __ access_store_at(T_FLOAT, IN_HEAP | IS_ARRAY, addr, noreg /* ftos */, noreg,
+                     noreg);
 }
 
 void TemplateTable::dastore() {
   transition(dtos, vtos);
   __ pop_i(rbx);
   // value is in UseSSE >= 2 ? xmm0 : ST(0)
   // rbx:  index
   // rdx:  array
   index_check(rdx, rbx); // prefer index in rbx
-  __ access_store_at(T_DOUBLE, IN_HEAP | IS_ARRAY,
-                     Address(rdx, rbx, Address::times_8,
-                             arrayOopDesc::base_offset_in_bytes(T_DOUBLE)),
-                     noreg /* dtos */, noreg, noreg);
+  Address addr(rdx, rbx, Address::times_8,
+               arrayOopDesc::base_offset_in_bytes(T_DOUBLE));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_write8));
+  __ access_store_at(T_DOUBLE, IN_HEAP | IS_ARRAY, addr, noreg /* dtos */,
+                     noreg, noreg);
 }
 
 void TemplateTable::aastore() {
   Label is_null, ok_is_subtype, done;
   transition(vtos, vtos);
@@ -1122,10 +1244,14 @@
 
   Address element_address(rdx, rcx,
                           UseCompressedOops? Address::times_4 : Address::times_ptr,
                           arrayOopDesc::base_offset_in_bytes(T_OBJECT));
 
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(
+      element_address, UseCompressedOops ? SharedRuntime::tsan_write4
+                                         : SharedRuntime::tsan_write8));
+
   index_check_without_pop(rdx, rcx);     // kills rbx
   __ testptr(rax, rax);
   __ jcc(Assembler::zero, is_null);
 
   // Move subklass into rbx
@@ -1180,27 +1306,27 @@
   __ testl(rcx, diffbit);
   Label L_skip;
   __ jccb(Assembler::zero, L_skip);
   __ andl(rax, 1);  // if it is a T_BOOLEAN array, mask the stored value to 0/1
   __ bind(L_skip);
-  __ access_store_at(T_BYTE, IN_HEAP | IS_ARRAY,
-                     Address(rdx, rbx,Address::times_1,
-                             arrayOopDesc::base_offset_in_bytes(T_BYTE)),
-                     rax, noreg, noreg);
+  Address addr(rdx, rbx, Address::times_1,
+               arrayOopDesc::base_offset_in_bytes(T_BYTE));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_write1));
+  __ access_store_at(T_BYTE, IN_HEAP | IS_ARRAY, addr, rax, noreg, noreg);
 }
 
 void TemplateTable::castore() {
   transition(itos, vtos);
   __ pop_i(rbx);
   // rax: value
   // rbx: index
   // rdx: array
   index_check(rdx, rbx);  // prefer index in rbx
-  __ access_store_at(T_CHAR, IN_HEAP | IS_ARRAY,
-                     Address(rdx, rbx, Address::times_2,
-                             arrayOopDesc::base_offset_in_bytes(T_CHAR)),
-                     rax, noreg, noreg);
+  Address addr(rdx, rbx, Address::times_2,
+               arrayOopDesc::base_offset_in_bytes(T_CHAR));
+  TSAN_RUNTIME_ONLY(tsan_observe_load_or_store(addr, SharedRuntime::tsan_write2));
+  __ access_store_at(T_CHAR, IN_HEAP | IS_ARRAY, addr, rax, noreg, noreg);
 }
 
 
 void TemplateTable::sastore() {
   castore();
@@ -2789,10 +2915,30 @@
                            in_bytes(cp_base_offset +
                                     ConstantPoolCacheEntry::f1_offset())));
     const int mirror_offset = in_bytes(Klass::java_mirror_offset());
     __ movptr(obj, Address(obj, mirror_offset));
     __ resolve_oop_handle(obj);
+    TSAN_RUNTIME_ONLY(
+      // Draw a happens-before edge from the class's static initializer to
+      // this lookup.
+
+      // java_lang_Class::_init_lock_offset may not have been initialized
+      // when generating code. It will be initialized at runtime though.
+      // So calculate its address and read from it at runtime.
+      __ pusha();
+      __ movq(c_rarg0, obj);
+      AddressLiteral init_lock_offset_address(
+          (address) java_lang_Class::init_lock_offset_addr(),
+          relocInfo::none);
+      __ lea(rax, init_lock_offset_address);
+      __ movl(rax, Address(rax, 0));
+      __ addq(c_rarg0, rax);
+      __ call_VM_leaf(CAST_FROM_FN_PTR(address,
+                                       SharedRuntime::tsan_acquire),
+                      c_rarg0);
+      __ popa();
+    );
   }
 }
 
 void TemplateTable::load_invoke_cp_cache_entry(int byte_no,
                                                Register method,
@@ -2884,10 +3030,15 @@
 
   if (!is_static) pop_and_check_object(obj);
 
   const Address field(obj, off, Address::times_1, 0*wordSize);
 
+  // During a TSAN instrumented run, move flags into rdx so we can later
+  // examine whether the field is volatile or has been annotated to be ignored
+  // by Tsan.
+  TSAN_RUNTIME_ONLY(__ movl(rdx, flags));
+
   Label Done, notByte, notBool, notInt, notShort, notChar, notLong, notFloat, notObj;
 
   __ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
   // Make sure we don't need to mask edx after the above shift
   assert(btos == 0, "change code, btos != 0");
@@ -2896,10 +3047,12 @@
 
   __ jcc(Assembler::notZero, notByte);
   // btos
   __ access_load_at(T_BYTE, IN_HEAP, rax, field, noreg, noreg);
   __ push(btos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read1, btos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_bgetfield, bc, rbx);
   }
   __ jmp(Done);
@@ -2909,10 +3062,12 @@
   __ jcc(Assembler::notEqual, notBool);
 
   // ztos (same code as btos)
   __ access_load_at(T_BOOLEAN, IN_HEAP, rax, field, noreg, noreg);
   __ push(ztos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read1, ztos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     // use btos rewriting, no truncating to t/f bit is needed for getfield.
     patch_bytecode(Bytecodes::_fast_bgetfield, bc, rbx);
   }
@@ -2922,10 +3077,14 @@
   __ cmpl(flags, atos);
   __ jcc(Assembler::notEqual, notObj);
   // atos
   do_oop_load(_masm, field, rax);
   __ push(atos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, UseCompressedOops ? SharedRuntime::tsan_read4
+                                    : SharedRuntime::tsan_read8,
+      atos));
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_agetfield, bc, rbx);
   }
   __ jmp(Done);
 
@@ -2933,10 +3092,12 @@
   __ cmpl(flags, itos);
   __ jcc(Assembler::notEqual, notInt);
   // itos
   __ access_load_at(T_INT, IN_HEAP, rax, field, noreg, noreg);
   __ push(itos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read4, itos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_igetfield, bc, rbx);
   }
   __ jmp(Done);
@@ -2945,10 +3106,12 @@
   __ cmpl(flags, ctos);
   __ jcc(Assembler::notEqual, notChar);
   // ctos
   __ access_load_at(T_CHAR, IN_HEAP, rax, field, noreg, noreg);
   __ push(ctos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read2, ctos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_cgetfield, bc, rbx);
   }
   __ jmp(Done);
@@ -2957,10 +3120,12 @@
   __ cmpl(flags, stos);
   __ jcc(Assembler::notEqual, notShort);
   // stos
   __ access_load_at(T_SHORT, IN_HEAP, rax, field, noreg, noreg);
   __ push(stos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read2, stos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_sgetfield, bc, rbx);
   }
   __ jmp(Done);
@@ -2971,10 +3136,12 @@
   // ltos
     // Generate code as if volatile (x86_32).  There just aren't enough registers to
     // save that information and this code is faster than the test.
   __ access_load_at(T_LONG, IN_HEAP | MO_RELAXED, noreg /* ltos */, field, noreg, noreg);
   __ push(ltos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read8, ltos));
   // Rewrite bytecode to be faster
   LP64_ONLY(if (!is_static && rc == may_rewrite) patch_bytecode(Bytecodes::_fast_lgetfield, bc, rbx));
   __ jmp(Done);
 
   __ bind(notLong);
@@ -2982,10 +3149,12 @@
   __ jcc(Assembler::notEqual, notFloat);
   // ftos
 
   __ access_load_at(T_FLOAT, IN_HEAP, noreg /* ftos */, field, noreg, noreg);
   __ push(ftos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read4, ftos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_fgetfield, bc, rbx);
   }
   __ jmp(Done);
@@ -2998,10 +3167,12 @@
 #endif
   // dtos
   // MO_RELAXED: for the case of volatile field, in fact it adds no extra work for the underlying implementation
   __ access_load_at(T_DOUBLE, IN_HEAP | MO_RELAXED, noreg /* dtos */, field, noreg, noreg);
   __ push(dtos);
+  TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+      field, rdx, SharedRuntime::tsan_read8, dtos));
   // Rewrite bytecode to be faster
   if (!is_static && rc == may_rewrite) {
     patch_bytecode(Bytecodes::_fast_dgetfield, bc, rbx);
   }
 #ifdef ASSERT
@@ -3133,12 +3304,10 @@
   // volatile_barrier(Assembler::Membar_mask_bits(Assembler::LoadStore |
   //                                              Assembler::StoreStore));
 
   Label notVolatile, Done;
   __ movl(rdx, flags);
-  __ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift);
-  __ andl(rdx, 0x1);
 
   // Check for volatile store
   __ testl(rdx, rdx);
   __ jcc(Assembler::zero, notVolatile);
 
@@ -3174,10 +3343,12 @@
 
   // btos
   {
     __ pop(btos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write1, btos));
     __ access_store_at(T_BYTE, IN_HEAP, field, rax, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_bputfield, bc, rbx, true, byte_no);
     }
     __ jmp(Done);
@@ -3189,10 +3360,12 @@
 
   // ztos
   {
     __ pop(ztos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write1, ztos));
     __ access_store_at(T_BOOLEAN, IN_HEAP, field, rax, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_zputfield, bc, rbx, true, byte_no);
     }
     __ jmp(Done);
@@ -3204,10 +3377,14 @@
 
   // atos
   {
     __ pop(atos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(field, rdx,
+        UseCompressedOops ? SharedRuntime::tsan_write4
+                          : SharedRuntime::tsan_write8,
+        atos));
     // Store into the field
     do_oop_store(_masm, field, rax);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_aputfield, bc, rbx, true, byte_no);
     }
@@ -3220,10 +3397,12 @@
 
   // itos
   {
     __ pop(itos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write4, itos));
     __ access_store_at(T_INT, IN_HEAP, field, rax, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_iputfield, bc, rbx, true, byte_no);
     }
     __ jmp(Done);
@@ -3235,10 +3414,12 @@
 
   // ctos
   {
     __ pop(ctos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write2, ctos));
     __ access_store_at(T_CHAR, IN_HEAP, field, rax, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_cputfield, bc, rbx, true, byte_no);
     }
     __ jmp(Done);
@@ -3250,10 +3431,12 @@
 
   // stos
   {
     __ pop(stos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write2, stos));
     __ access_store_at(T_SHORT, IN_HEAP, field, rax, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_sputfield, bc, rbx, true, byte_no);
     }
     __ jmp(Done);
@@ -3265,10 +3448,12 @@
 
   // ltos
   {
     __ pop(ltos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write8, ltos));
     // MO_RELAXED: generate atomic store for the case of volatile field (important for x86_32)
     __ access_store_at(T_LONG, IN_HEAP | MO_RELAXED, field, noreg /* ltos*/, noreg, noreg);
 #ifdef _LP64
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_lputfield, bc, rbx, true, byte_no);
@@ -3283,10 +3468,12 @@
 
   // ftos
   {
     __ pop(ftos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write4, ftos));
     __ access_store_at(T_FLOAT, IN_HEAP, field, noreg /* ftos */, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_fputfield, bc, rbx, true, byte_no);
     }
     __ jmp(Done);
@@ -3301,10 +3488,12 @@
 
   // dtos
   {
     __ pop(dtos);
     if (!is_static) pop_and_check_object(obj);
+    TSAN_RUNTIME_ONLY(tsan_observe_get_or_put(
+        field, rdx, SharedRuntime::tsan_write8, dtos));
     // MO_RELAXED: for the case of volatile field, in fact it adds no extra work for the underlying implementation
     __ access_store_at(T_DOUBLE, IN_HEAP | MO_RELAXED, field, noreg /* dtos */, noreg, noreg);
     if (!is_static && rc == may_rewrite) {
       patch_bytecode(Bytecodes::_fast_dputfield, bc, rbx, true, byte_no);
     }
@@ -4124,10 +4313,18 @@
       __ call_VM_leaf(
            CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc), rax);
       __ pop(atos);
     }
 
+    TSAN_RUNTIME_ONLY(
+      // return value of new oop is in rax.
+      __ push(atos);
+      __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::tsan_track_obj),
+                      rax);
+      __ pop(atos);
+    );
+
     __ jmp(done);
   }
 
   // slow case
   __ bind(slow_case);
diff a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
--- a/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
+++ b/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp
@@ -822,11 +822,12 @@
 }
 
 #ifndef PRODUCT
 void os::verify_stack_alignment() {
 #ifdef AMD64
-  assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment");
+  // TODO: TSAN requires being built with Clang, but stack alignment assertion fails with Clang.
+  // assert(((intptr_t)os::current_stack_pointer() & (StackAlignmentInBytes-1)) == 0, "incorrect stack alignment");
 #endif
 }
 #endif
 
 
diff a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -34,10 +34,13 @@
 #include "classfile/javaClasses.inline.hpp"
 #include "classfile/moduleEntry.hpp"
 #include "classfile/packageEntry.hpp"
 #include "classfile/symbolTable.hpp"
 #include "classfile/systemDictionary.hpp"
+#if INCLUDE_TSAN
+#include "classfile/tsanIgnoreList.hpp"
+#endif // INCLUDE_TSAN
 #include "classfile/verificationType.hpp"
 #include "classfile/verifier.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
@@ -1077,10 +1080,11 @@
     _method_LambdaForm_Compiled,
     _method_Hidden,
     _method_HotSpotIntrinsicCandidate,
     _jdk_internal_vm_annotation_Contended,
     _field_Stable,
+    _field_TsanIgnore,
     _jdk_internal_vm_annotation_ReservedStackAccess,
     _annotation_LIMIT
   };
   const Location _location;
   int _annotations_present;
@@ -1113,10 +1117,15 @@
 
   bool is_contended() const { return has_annotation(_jdk_internal_vm_annotation_Contended); }
 
   void set_stable(bool stable) { set_annotation(_field_Stable); }
   bool is_stable() const { return has_annotation(_field_Stable); }
+
+#if INCLUDE_TSAN
+  void set_tsan_ignore(bool tsan_ignore) { set_annotation(_field_TsanIgnore); }
+  bool is_tsan_ignore() const { return has_annotation(_field_TsanIgnore); }
+#endif  // INCLUDE_TSAN
 };
 
 // This class also doubles as a holder for metadata cleanup.
 class ClassFileParser::FieldAnnotationCollector : public AnnotationCollector {
 private:
@@ -1685,10 +1694,17 @@
 
     // Remember how many oops we encountered and compute allocation type
     const FieldAllocationType atype = fac->update(is_static, type);
     field->set_allocation_type(atype);
 
+    TSAN_RUNTIME_ONLY(
+      if (ThreadSanitizerIgnoreFile != NULL &&
+          TsanIgnoreList::match(_class_name, name, type)) {
+        parsed_annotations.set_tsan_ignore(true);
+      }
+    );
+
     // After field is initialized with type, we can augment it with aux info
     if (parsed_annotations.has_any_annotations()) {
       parsed_annotations.apply_to(field);
       if (field->is_contended()) {
         _has_contended_fields = true;
@@ -2118,10 +2134,18 @@
     case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ReservedStackAccess_signature): {
       if (_location != _in_method)  break;  // only allow for methods
       if (RestrictReservedStack && !privileged) break; // honor privileges
       return _jdk_internal_vm_annotation_ReservedStackAccess;
     }
+#if INCLUDE_TSAN
+    case vmSymbols::VM_SYMBOL_ENUM_NAME(java_util_concurrent_annotation_LazyInit): {
+      if (_location != _in_field) {
+        break;  // only allow for fields
+      }
+      return _field_TsanIgnore;
+    }
+#endif  // INCLUDE_TSAN
     default: {
       break;
     }
   }
   return AnnotationCollector::_unknown;
@@ -2130,10 +2154,15 @@
 void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) {
   if (is_contended())
     f->set_contended_group(contended_group());
   if (is_stable())
     f->set_stable(true);
+  TSAN_RUNTIME_ONLY(
+    if (is_tsan_ignore())
+      f->set_tsan_ignore(true);
+  );
+
 }
 
 ClassFileParser::FieldAnnotationCollector::~FieldAnnotationCollector() {
   // If there's an error deallocate metadata for field annotations
   MetadataFactory::free_array<u1>(_loader_data, _field_annotations);
@@ -5806,10 +5835,27 @@
     }
   }
 
   ClassLoadingService::notify_class_loaded(ik, false /* not shared class */);
 
+#if INCLUDE_TSAN
+  if (ThreadSanitizer && !ik->is_interface()) {
+    ik->ensure_space_for_methodids(0);
+    int num_methods = ik->methods()->length();
+    for (int index = 0; index < num_methods; index++) {
+      // Make sure each method has a jmethodID.
+      // This allows us to avoid allocating jmethodIDs during program execution.
+      jmethodID id = ik->methods()->at(index)->jmethod_id();
+#ifdef ASSERT
+      u8 id_u8 = reinterpret_cast<u8>(id);
+      assert((id_u8 & right_n_bits(3)) == 0, "jmethodID is not aligned");
+      assert((id_u8 & left_n_bits(17)) == 0, "jmethodID is not aligned");
+#endif
+    }
+  }
+#endif // INCLUDE_TSAN
+
   if (!is_internal()) {
     if (log_is_enabled(Info, class, load)) {
       ResourceMark rm;
       const char* module_name = (module_entry->name() == NULL) ? UNNAMED_MODULE : module_entry->name()->as_C_string();
       ik->print_class_load_logging(_loader_data, module_name, _stream);
diff a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -1381,10 +1381,18 @@
 
 oop java_lang_Class::init_lock(oop java_class) {
   assert(_init_lock_offset != 0, "must be set");
   return java_class->obj_field(_init_lock_offset);
 }
+
+#if INCLUDE_TSAN
+oop* java_lang_Class::init_lock_addr(oop java_class) {
+  assert(_init_lock_offset != 0, "must be set");
+  return java_class->obj_field_addr_raw<oop>(_init_lock_offset);
+}
+#endif  // INCLUDE_TSAN
+
 void java_lang_Class::set_init_lock(oop java_class, oop init_lock) {
   assert(_init_lock_offset != 0, "must be set");
   java_class->obj_field_put(_init_lock_offset, init_lock);
 }
 
diff a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp
--- a/src/hotspot/share/classfile/javaClasses.hpp
+++ b/src/hotspot/share/classfile/javaClasses.hpp
@@ -325,10 +325,14 @@
   static void set_classRedefinedCount(oop the_class_mirror, int value);
 
   // Support for embedded per-class oops
   static oop  protection_domain(oop java_class);
   static oop  init_lock(oop java_class);
+#if INCLUDE_TSAN
+  static oop* init_lock_addr(oop java_class);
+  static const int* init_lock_offset_addr() { return &_init_lock_offset; }
+#endif  // INCLUDE_TSAN
   static oop  component_mirror(oop java_class);
   static objArrayOop  signers(oop java_class);
   static void set_signers(oop java_class, objArrayOop signers);
 
   static oop class_loader(oop java_class);
diff a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -536,14 +536,21 @@
   bool calledholdinglock
       = ObjectSynchronizer::current_thread_holds_lock((JavaThread*)THREAD, lockObject);
   assert(calledholdinglock,"must hold lock for notify");
   assert((lockObject() != _system_loader_lock_obj && !is_parallelCapable(lockObject)), "unexpected double_lock_wait");
   ObjectSynchronizer::notifyall(lockObject, THREAD);
+
+  TSAN_ONLY(int tsan_rec = 0;)
+  TSAN_RUNTIME_ONLY(
+    tsan_rec = SharedRuntime::tsan_oop_rec_unlock(THREAD, lockObject());
+    assert(tsan_rec > 0, "tsan: unlocking unlocked mutex");
+  );
   intx recursions =  ObjectSynchronizer::complete_exit(lockObject, THREAD);
   SystemDictionary_lock->wait();
   SystemDictionary_lock->unlock();
   ObjectSynchronizer::reenter(lockObject, recursions, THREAD);
+  TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_rec_lock(THREAD, lockObject(), tsan_rec));
   SystemDictionary_lock->lock();
 }
 
 // If the class in is in the placeholder table, class loading is in progress
 // For cases where the application changes threads to load classes, it
diff a/src/hotspot/share/classfile/tsanIgnoreList.cpp b/src/hotspot/share/classfile/tsanIgnoreList.cpp
--- /dev/null
+++ b/src/hotspot/share/classfile/tsanIgnoreList.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "classfile/tsanIgnoreList.hpp"
+#include "classfile/symbolTable.hpp"
+#include "memory/resourceArea.inline.hpp"
+
+static const int MAX_LINE_SIZE  = 1024;
+
+class FieldMatcher : public CHeapObj<mtClass> {
+ public:
+  enum Mode {
+    Exact = 0,
+    Prefix = 1,
+    Any = 2,
+    Unknown = -1
+  };
+
+  FieldMatcher(const Symbol* class_name, Mode class_mode,
+               const Symbol* field_name, Mode field_mode, FieldMatcher* next)
+      : _class_name(class_name),
+        _field_name(field_name),
+        _class_mode(class_mode),
+        _field_mode(field_mode),
+        _next(next) { }
+
+  // Given a FieldMatcher as the head of linked-list, returns true if any
+  // FieldMatcher in the list matches.
+  static bool match_any(FieldMatcher* head,
+                        const Symbol* class_name,
+                        const Symbol* field_name) {
+    while (head) {
+      if (head->match(class_name, field_name)) {
+        return true;
+      }
+      head = head->_next;
+    }
+    return false;
+  }
+
+ protected:
+  const Symbol* _class_name;
+  const Symbol* _field_name;
+  Mode _class_mode;
+  Mode _field_mode;
+  FieldMatcher* _next;
+
+  static bool match(const Symbol* candidate, const Symbol* match, Mode mode) {
+    ResourceMark rm;
+    switch (mode) {
+      case Exact:
+        return candidate == match;
+      case Prefix: {
+        const char* candidate_str = candidate->as_C_string();
+        const char* match_str = match->as_C_string();
+        return (strstr(candidate_str, match_str) == candidate_str);
+      }
+      case Any:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  bool match(const Symbol* class_name, const Symbol* field_name) {
+    return (match(class_name, _class_name, _class_mode) &&
+            match(field_name, _field_name, _field_mode));
+  }
+};
+
+FieldMatcher* TsanIgnoreList::_exact_match = NULL;
+FieldMatcher* TsanIgnoreList::_prefix_match = NULL;
+
+// Detects the pattern-matching mode based on the presence and location of
+// wildcard character, fixes the pattern inplace and returns the
+// pattern-matching mode.
+static FieldMatcher::Mode make_pattern(char* pattern) {
+  const int len = strlen(pattern);
+  // Inverse of Symbol::as_klass_external_name.
+  // Turn all '.'s into '/'s.
+  for (int index = 0; index < len; index++) {
+    if (pattern[index] == '.') {
+      pattern[index] = '/';
+    }
+  }
+
+  char* asterisk = strstr(pattern, "*");
+  if (asterisk == NULL) {
+    return FieldMatcher::Exact;
+  }
+  if (asterisk - pattern != len - 1) {
+    warning("Unexpected location for '*' in \"%s\". "
+            "Only prefix patterns are supported.", pattern);
+  }
+  if (asterisk == pattern) {
+    return FieldMatcher::Any;
+  }
+  pattern[len - 1] = '\0';
+  return FieldMatcher::Prefix;
+}
+
+void TsanIgnoreList::parse_from_line(char* line) {
+  EXCEPTION_MARK;
+  char class_pattern[MAX_LINE_SIZE], field_pattern[MAX_LINE_SIZE];
+  // Replace '#' with '\0'.
+  {
+    char* comment = strchr(line, '#');
+    if (comment != NULL) {
+      *comment = '\0';
+    }
+  }
+  // Parse line.
+  if (sscanf(line, "%s %s", class_pattern, field_pattern) != 2) {
+    return;
+  }
+  // Get matcher mode from pattern.
+  FieldMatcher::Mode class_mode = make_pattern(class_pattern);
+  FieldMatcher::Mode field_mode = make_pattern(field_pattern);
+  // If we match against Any, no need for a symbol, else create the symbol.
+  Symbol* class_symbol = (class_mode == FieldMatcher::Any) ? NULL :
+      SymbolTable::new_symbol(class_pattern);
+  Symbol* field_symbol = (field_mode == FieldMatcher::Any) ? NULL :
+      SymbolTable::new_symbol(field_pattern);
+  // Add matcher to beginning of linked list.
+  if (class_mode == FieldMatcher::Exact && field_mode == FieldMatcher::Exact) {
+    _exact_match = new FieldMatcher(class_symbol, class_mode, field_symbol,
+                                    field_mode, _exact_match);
+  } else {
+    _prefix_match = new FieldMatcher(class_symbol, class_mode, field_symbol,
+                                     field_mode, _prefix_match);
+  }
+}
+
+void TsanIgnoreList::parse_from_file(FILE* stream) {
+  char line[MAX_LINE_SIZE];
+  while (fgets(line, sizeof(line), stream)) {
+    if (strlen(line) == sizeof(line) - 1) {
+      warning("TSAN ignore file (ThreadSanitizerIgnoreFile) contains a line longer "
+              "than %d. This pattern will be truncated, and the rest of the "
+              "file will not be processed for pattern matching.",
+              MAX_LINE_SIZE);
+      break;
+    }
+    parse_from_line(line);
+  }
+  if (ferror(stream)) {
+    warning("Error reading from TSAN ignore file");
+  }
+}
+
+void TsanIgnoreList::init() {
+  if (ThreadSanitizerIgnoreFile == NULL) {
+    return;
+  }
+
+  FILE* stream = fopen(ThreadSanitizerIgnoreFile, "rt");
+  if (stream == NULL) {
+    warning("TSAN ignore file (ThreadSanitizerIgnoreFile:%s) not found.",
+            ThreadSanitizerIgnoreFile);
+    return;
+  }
+  parse_from_file(stream);
+  fclose(stream);
+}
+
+bool TsanIgnoreList::match(
+    const Symbol* class_name, const Symbol* field_name,
+    BasicType type) {
+  // Wildcard matches are only for primitive types. References should be
+  // added to list individually since they become release/acquire.
+  if (is_java_primitive(type) &&
+      FieldMatcher::match_any(_prefix_match, class_name, field_name)) {
+    return true;
+  }
+  return FieldMatcher::match_any(_exact_match, class_name, field_name);
+}
diff a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -230,10 +230,11 @@
   template(java_util_concurrent_locks_AbstractOwnableSynchronizer,           "java/util/concurrent/locks/AbstractOwnableSynchronizer") \
   template(java_util_concurrent_atomic_AtomicIntegerFieldUpdater_Impl,       "java/util/concurrent/atomic/AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl") \
   template(java_util_concurrent_atomic_AtomicLongFieldUpdater_CASUpdater,    "java/util/concurrent/atomic/AtomicLongFieldUpdater$CASUpdater") \
   template(java_util_concurrent_atomic_AtomicLongFieldUpdater_LockedUpdater, "java/util/concurrent/atomic/AtomicLongFieldUpdater$LockedUpdater") \
   template(java_util_concurrent_atomic_AtomicReferenceFieldUpdater_Impl,     "java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl") \
+  template(java_util_concurrent_annotation_LazyInit,                         "Ljava/util/concurrent/annotation/LazyInit;") \
   template(jdk_internal_vm_annotation_Contended_signature,                   "Ljdk/internal/vm/annotation/Contended;")    \
   template(jdk_internal_vm_annotation_ReservedStackAccess_signature,         "Ljdk/internal/vm/annotation/ReservedStackAccess;") \
                                                                                                   \
   /* class symbols needed by intrinsics */                                                        \
   VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, template, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \
diff a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp
@@ -201,10 +201,14 @@
 
   // Stop and resume concurrent GC threads interfering with safepoint operations
   virtual void safepoint_synchronize_begin() {}
   virtual void safepoint_synchronize_end() {}
 
+  // TODO(tsan): _reserved MemRegion is not available to all collectors.
+  // Should we support collectors without _reserved MemRegion? See 8224815.
+  TSAN_ONLY(MemRegion reserved_region() const { return _reserved; })
+
   void initialize_reserved_region(const ReservedHeapSpace& rs);
 
   virtual size_t capacity() const = 0;
   virtual size_t used() const = 0;
 
diff a/src/hotspot/share/gc/shared/memAllocator.cpp b/src/hotspot/share/gc/shared/memAllocator.cpp
--- a/src/hotspot/share/gc/shared/memAllocator.cpp
+++ b/src/hotspot/share/gc/shared/memAllocator.cpp
@@ -247,10 +247,13 @@
 void MemAllocator::Allocation::notify_allocation() {
   notify_allocation_low_memory_detector();
   notify_allocation_jfr_sampler();
   notify_allocation_dtrace_sampler();
   notify_allocation_jvmti_sampler();
+  TSAN_RUNTIME_ONLY(
+      SharedRuntime::tsan_track_obj_with_size(obj(), (int)_allocator._word_size);
+  );
 }
 
 HeapWord* MemAllocator::allocate_outside_tlab(Allocation& allocation) const {
   allocation._allocated_outside_tlab = true;
   HeapWord* mem = Universe::heap()->mem_allocate(_word_size, &allocation._overhead_limit_exceeded);
diff a/src/hotspot/share/gc/shared/weakProcessorPhases.cpp b/src/hotspot/share/gc/shared/weakProcessorPhases.cpp
--- a/src/hotspot/share/gc/shared/weakProcessorPhases.cpp
+++ b/src/hotspot/share/gc/shared/weakProcessorPhases.cpp
@@ -34,15 +34,19 @@
 
 #if INCLUDE_JVMTI
 #include "prims/jvmtiExport.hpp"
 #endif // INCLUDE_JVMTI
 
+#if INCLUDE_TSAN
+#include "tsan/tsanOopMap.hpp"
+#endif // INCLUDE_TSAN
+
 // serial_phase_count is 0 if JFR and JVMTI are both not built,
 // requiring some code to be careful to avoid tautological checks
 // that some compilers warn about.
 
-#define HAVE_SERIAL_PHASES (INCLUDE_JVMTI || INCLUDE_JFR)
+#define HAVE_SERIAL_PHASES (INCLUDE_JVMTI || INCLUDE_JFR || INCLUDE_TSAN)
 
 WeakProcessorPhases::Phase WeakProcessorPhases::serial_phase(uint value) {
 #if HAVE_SERIAL_PHASES
   assert(value < serial_phase_count, "Invalid serial phase value %u", value);
   return static_cast<Phase>(value + serial_phase_start);
@@ -109,20 +113,22 @@
 
 const char* WeakProcessorPhases::description(Phase phase) {
   switch (phase) {
   JVMTI_ONLY(case jvmti: return "JVMTI weak processing";)
   JFR_ONLY(case jfr: return "JFR weak processing";)
+  TSAN_ONLY(case tsan: return "TSAN weak processing";)
   default:
     ShouldNotReachHere();
     return "Invalid serial weak processing phase";
   }
 }
 
 WeakProcessorPhases::Processor WeakProcessorPhases::processor(Phase phase) {
   switch (phase) {
   JVMTI_ONLY(case jvmti: return &JvmtiExport::weak_oops_do;)
   JFR_ONLY(case jfr: return &Jfr::weak_oops_do;)
+  TSAN_ONLY(case tsan: return &TsanOopMap::weak_oops_do;)
   default:
     ShouldNotReachHere();
     return NULL;
   }
 }
diff a/src/hotspot/share/gc/shared/weakProcessorPhases.hpp b/src/hotspot/share/gc/shared/weakProcessorPhases.hpp
--- a/src/hotspot/share/gc/shared/weakProcessorPhases.hpp
+++ b/src/hotspot/share/gc/shared/weakProcessorPhases.hpp
@@ -41,17 +41,18 @@
   typedef void (*Processor)(BoolObjectClosure*, OopClosure*);
 
   enum Phase {
     // Serial phases.
     JVMTI_ONLY(jvmti JFR_ONLY(COMMA))
-    JFR_ONLY(jfr)
+    JFR_ONLY(jfr TSAN_ONLY(COMMA))
+    TSAN_ONLY(tsan)
 
     // Additional implicit phase values follow for oopstorages.
   };
 
   static const uint serial_phase_start = 0;
-  static const uint serial_phase_count = 0 JVMTI_ONLY(+ 1) JFR_ONLY(+ 1);
+  static const uint serial_phase_count = 0 JVMTI_ONLY(+ 1) JFR_ONLY(+ 1) TSAN_ONLY(+ 1);
   static const uint oopstorage_phase_start = serial_phase_count;
   static const uint oopstorage_phase_count = OopStorageSet::weak_count;
   static const uint phase_count = serial_phase_count + oopstorage_phase_count;
 
   // Precondition: value < serial_phase_count
diff a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahPhaseTimings.hpp
@@ -343,10 +343,11 @@
   f(CodeCacheRoots,           "CodeCache Roots (ms):")           \
   f(UniverseRoots,            "Universe Roots (ms):")            \
   f(JNIRoots,                 "JNI Handles Roots (ms):")         \
   f(JVMTIWeakRoots,           "JVMTI Weak Roots (ms):")          \
   f(JFRWeakRoots,             "JFR Weak Roots (ms):")            \
+  f(TSANWeakRoots,            "TSAN Weak Roots (ms):")           \
   f(JNIWeakRoots,             "JNI Weak Roots (ms):")            \
   f(StringTableRoots,         "StringTable Roots(ms):")          \
   f(ResolvedMethodTableRoots, "Resolved Table Roots(ms):")       \
   f(VMGlobalRoots,            "VM Global Roots(ms)")             \
   f(VMWeakRoots,              "VM Weak Roots(ms)")               \
diff a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.cpp
@@ -40,10 +40,11 @@
 #include "memory/iterator.hpp"
 #include "memory/resourceArea.hpp"
 #include "memory/universe.hpp"
 #include "runtime/thread.hpp"
 #include "services/management.hpp"
+#include "tsan/tsanOopMap.hpp"
 
 ShenandoahSerialRoot::ShenandoahSerialRoot(ShenandoahSerialRoot::OopsDo oops_do, ShenandoahPhaseTimings::GCParPhases phase) :
   _oops_do(oops_do), _phase(phase) {
 }
 
@@ -98,13 +99,20 @@
 ShenandoahJFRWeakRoot::ShenandoahJFRWeakRoot() :
   ShenandoahWeakSerialRoot(&Jfr::weak_oops_do, ShenandoahPhaseTimings::JFRWeakRoots) {
 }
 #endif // INCLUDE_JFR
 
+#if INCLUDE_TSAN
+ShenandoahTSANWeakRoot::ShenandoahTSANWeakRoot() :
+  ShenandoahWeakSerialRoot(&TsanOopMap::weak_oops_do, ShenandoahPhaseTimings::TSANWeakRoots) {
+}
+#endif // INCLUDE_TSAN
+
 void ShenandoahSerialWeakRoots::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id) {
   JVMTI_ONLY(_jvmti_weak_roots.weak_oops_do(is_alive, keep_alive, worker_id);)
   JFR_ONLY(_jfr_weak_roots.weak_oops_do(is_alive, keep_alive, worker_id);)
+  TSAN_ONLY(_tsan_weak_roots.weak_oops_do(is_alive, keep_alive, worker_id);)
 }
 
 void ShenandoahSerialWeakRoots::weak_oops_do(OopClosure* cl, uint worker_id) {
   AlwaysTrueClosure always_true;
   weak_oops_do(&always_true, cl, worker_id);
diff a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp
--- a/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp
+++ b/src/hotspot/share/gc/shenandoah/shenandoahRootProcessor.hpp
@@ -82,14 +82,22 @@
 public:
   ShenandoahJFRWeakRoot();
 };
 #endif // INCLUDE_JFR
 
+#if INCLUDE_TSAN
+class ShenandoahTSANWeakRoot : public ShenandoahWeakSerialRoot {
+public:
+  ShenandoahTSANWeakRoot();
+};
+#endif // INCLUDE_TSAN
+
 class ShenandoahSerialWeakRoots {
 private:
   JVMTI_ONLY(ShenandoahJVMTIWeakRoot _jvmti_weak_roots;)
   JFR_ONLY(ShenandoahJFRWeakRoot     _jfr_weak_roots;)
+  TSAN_ONLY(ShenandoahTSANWeakRoot   _tsan_weak_roots;)
 public:
   void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id);
   void weak_oops_do(OopClosure* cl, uint worker_id);
 };
 
diff a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -280,10 +280,16 @@
 JVM_GetSystemPackage(JNIEnv *env, jstring name);
 
 JNIEXPORT jobjectArray JNICALL
 JVM_GetSystemPackages(JNIEnv *env);
 
+/*
+ * java.lang.ref.Finalizer
+ */
+JNIEXPORT jboolean JNICALL
+JVM_GetTsanEnabled(JNIEnv *env);
+
 /*
  * java.lang.ref.Reference
  */
 JNIEXPORT jobject JNICALL
 JVM_GetAndClearReferencePendingList(JNIEnv *env);
diff a/src/hotspot/share/interpreter/interpreterRuntime.cpp b/src/hotspot/share/interpreter/interpreterRuntime.cpp
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp
@@ -737,19 +737,25 @@
     if ((is_put && !has_initialized_final_update) || !info.access_flags().is_final()) {
       put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
     }
   }
 
+  bool is_tsan_ignore = false;
+#if INCLUDE_TSAN
+  is_tsan_ignore = info.access_flags().is_stable() || info.access_flags().is_tsan_ignore();
+#endif  // INCLUDE_TSAN
+
   cp_cache_entry->set_field(
     get_code,
     put_code,
     info.field_holder(),
     info.index(),
     info.offset(),
     state,
     info.access_flags().is_final(),
     info.access_flags().is_volatile(),
+    is_tsan_ignore,
     pool->pool_holder()
   );
 }
 
 
diff a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp
--- a/src/hotspot/share/oops/cpCache.cpp
+++ b/src/hotspot/share/oops/cpCache.cpp
@@ -130,18 +130,20 @@
                                        int field_index,
                                        int field_offset,
                                        TosState field_type,
                                        bool is_final,
                                        bool is_volatile,
+                                       bool is_tsan_ignore,
                                        Klass* root_klass) {
   set_f1(field_holder);
   set_f2(field_offset);
   assert((field_index & field_index_mask) == field_index,
          "field index does not fit in low flag bits");
   set_field_flags(field_type,
                   ((is_volatile ? 1 : 0) << is_volatile_shift) |
-                  ((is_final    ? 1 : 0) << is_final_shift),
+                  ((is_final    ? 1 : 0) << is_final_shift) |
+                  ((is_tsan_ignore ? 1 : 0) << is_tsan_ignore_shift),
                   field_index);
   set_bytecode_1(get_code);
   set_bytecode_2(put_code);
   NOT_PRODUCT(verify(tty));
 }
diff a/src/hotspot/share/oops/cpCache.hpp b/src/hotspot/share/oops/cpCache.hpp
--- a/src/hotspot/share/oops/cpCache.hpp
+++ b/src/hotspot/share/oops/cpCache.hpp
@@ -179,10 +179,11 @@
     // high order bits are the TosState corresponding to field type or method return type
     tos_state_bits             = 4,
     tos_state_mask             = right_n_bits(tos_state_bits),
     tos_state_shift            = BitsPerInt - tos_state_bits,  // see verify_tos_state_shift below
     // misc. option bits; can be any bit position in [16..27]
+    is_tsan_ignore_shift       = 27,  // Should the field be ignored by TSAN?
     is_field_entry_shift       = 26,  // (F) is it a field or a method?
     has_local_signature_shift  = 25,  // (S) does the call site have a per-site signature (sig-poly methods)?
     has_appendix_shift         = 24,  // (A) does the call site have an appendix argument?
     is_forced_virtual_shift    = 23,  // (I) is the interface reference forced to virtual mode?
     is_final_shift             = 22,  // (f) is the field or method final?
@@ -222,10 +223,11 @@
     int             orig_field_index,            // the original field index in the field holder
     int             field_offset,                // the field offset in words in the field holder
     TosState        field_type,                  // the (machine) field type
     bool            is_final,                    // the field is final
     bool            is_volatile,                 // the field is volatile
+    bool            is_tsan_ignore,              // the field should be ignored by TSAN
     Klass*          root_klass                   // needed by the GC to dirty the klass
   );
 
  private:
   void set_direct_or_vtable_call(
diff a/src/hotspot/share/oops/fieldInfo.hpp b/src/hotspot/share/oops/fieldInfo.hpp
--- a/src/hotspot/share/oops/fieldInfo.hpp
+++ b/src/hotspot/share/oops/fieldInfo.hpp
@@ -246,10 +246,20 @@
   void set_stable(bool z) {
     if (z) _shorts[access_flags_offset] |=  JVM_ACC_FIELD_STABLE;
     else   _shorts[access_flags_offset] &= ~JVM_ACC_FIELD_STABLE;
   }
 
+#if INCLUDE_TSAN
+  bool is_tsan_ignore() const {
+    return (access_flags() & JVM_ACC_FIELD_TSAN_IGNORE) != 0;
+  }
+  void set_tsan_ignore(bool z) {
+    if (z) _shorts[access_flags_offset] |=  JVM_ACC_FIELD_TSAN_IGNORE;
+    else   _shorts[access_flags_offset] &= ~JVM_ACC_FIELD_TSAN_IGNORE;
+  }
+#endif  // INCLUDE_TSAN
+
   Symbol* lookup_symbol(int symbol_index) const {
     assert(is_internal(), "only internal fields");
     return vmSymbols::symbol_at((vmSymbols::SID)symbol_index);
   }
 };
diff a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -86,10 +86,13 @@
 #include "c1/c1_Compiler.hpp"
 #endif
 #if INCLUDE_JFR
 #include "jfr/jfrEvents.hpp"
 #endif
+#if INCLUDE_TSAN
+#include "runtime/sharedRuntime.hpp"
+#endif
 
 
 #ifdef DTRACE_ENABLED
 
 
@@ -712,10 +715,17 @@
     // progress, whereas here we might just be spinning in place.
     if (old_state != _init_state)
       set_init_state(old_state);
   } else {
     // linking successfull, mark class as initialized
+    TSAN_RUNTIME_ONLY(
+      // Construct a happens-before edge between the write of _init_state to
+      // fully_initialized and the later checking if it's initialized.
+      void* const lock_address = reinterpret_cast<void*>(
+          java_lang_Class::init_lock_addr(java_mirror()));
+      SharedRuntime::tsan_release(lock_address);
+    );
     set_init_state(fully_initialized);
     fence_and_clear_init_lock();
     // trace
     if (log_is_enabled(Info, class, init)) {
       ResourceMark rm(THREAD);
@@ -733,10 +743,17 @@
     initialize_impl(CHECK);
     // Note: at this point the class may be initialized
     //       OR it may be in the state of being initialized
     //       in case of recursive initialization!
   } else {
+    TSAN_RUNTIME_ONLY(
+      // Construct a happens-before edge between the write of _init_state to
+      // fully_initialized and here.
+      void* const lock_address = reinterpret_cast<void*>(
+          java_lang_Class::init_lock_addr(java_mirror()));
+      SharedRuntime::tsan_acquire(lock_address);
+    );
     assert(is_initialized(), "sanity check");
   }
 }
 
 
@@ -1093,10 +1110,17 @@
 
 void InstanceKlass::set_initialization_state_and_notify(ClassState state, TRAPS) {
   Handle h_init_lock(THREAD, init_lock());
   if (h_init_lock() != NULL) {
     ObjectLocker ol(h_init_lock, THREAD);
+    TSAN_RUNTIME_ONLY(
+      // Construct a happens-before edge between the write of _init_state to
+      // fully_initialized and the later checking if it's initialized.
+      void* const lock_address = reinterpret_cast<void*>(
+          java_lang_Class::init_lock_addr(java_mirror()));
+      SharedRuntime::tsan_release(lock_address);
+    );
     set_init_thread(NULL); // reset _init_thread before changing _init_state
     set_init_state(state);
     fence_and_clear_init_lock();
     ol.notify_all(CHECK);
   } else {
diff a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -80,10 +80,13 @@
 #include "runtime/vmOperations.hpp"
 #include "runtime/vm_version.hpp"
 #include "services/attachListener.hpp"
 #include "services/management.hpp"
 #include "services/threadService.hpp"
+#if INCLUDE_TSAN
+#include "tsan/tsan.hpp"
+#endif  // INCLUDE_TSAN
 #include "utilities/copy.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/events.hpp"
 #include "utilities/histogram.hpp"
@@ -3201,10 +3204,19 @@
   objArrayOop result = ClassLoader::get_system_packages(CHECK_NULL);
   return (jobjectArray) JNIHandles::make_local(result);
 JVM_END
 
 
+// java.lang.ref.Finalizer ///////////////////////////////////////////////////////////////
+
+JVM_ENTRY(jboolean, JVM_GetTsanEnabled(JNIEnv *env))
+  JVMWrapper("JVM_GetTsanEnabled");
+  TSAN_ONLY(return ThreadSanitizer;)
+  NOT_TSAN(return JNI_FALSE;)
+JVM_END
+
+
 // java.lang.ref.Reference ///////////////////////////////////////////////////////////////
 
 
 JVM_ENTRY(jobject, JVM_GetAndClearReferencePendingList(JNIEnv* env))
   JVMWrapper("JVM_GetAndClearReferencePendingList");
@@ -3423,32 +3435,37 @@
 
 
 JNIEXPORT void* JNICALL JVM_RawMonitorCreate(void) {
   VM_Exit::block_if_vm_exited();
   JVMWrapper("JVM_RawMonitorCreate");
-  return new os::PlatformMutex();
+  void *mon = new os::PlatformMutex();
+  TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_CREATE(mon));
+  return mon;
 }
 
 
 JNIEXPORT void JNICALL  JVM_RawMonitorDestroy(void *mon) {
   VM_Exit::block_if_vm_exited();
   JVMWrapper("JVM_RawMonitorDestroy");
+  TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_DESTROY(mon));
   delete ((os::PlatformMutex*) mon);
 }
 
 
 JNIEXPORT jint JNICALL JVM_RawMonitorEnter(void *mon) {
   VM_Exit::block_if_vm_exited();
   JVMWrapper("JVM_RawMonitorEnter");
   ((os::PlatformMutex*) mon)->lock();
+  TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_ACQUIRED(mon));
   return 0;
 }
 
 
 JNIEXPORT void JNICALL JVM_RawMonitorExit(void *mon) {
   VM_Exit::block_if_vm_exited();
   JVMWrapper("JVM_RawMonitorExit");
+  TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_RELEASED(mon));
   ((os::PlatformMutex*) mon)->unlock();
 }
 
 
 // Shared JNI/JVM entry points //////////////////////////////////////////////////////////////
diff a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -71,10 +71,13 @@
 #include "runtime/threadSMR.hpp"
 #include "runtime/timerTrace.hpp"
 #include "runtime/vframe.inline.hpp"
 #include "runtime/vmThread.hpp"
 #include "services/threadService.hpp"
+#if INCLUDE_TSAN
+#include "tsan/tsan.hpp"
+#endif  // INCLUDE_TSAN
 #include "utilities/exceptions.hpp"
 #include "utilities/preserveException.hpp"
 #include "utilities/utf8.hpp"
 
 
@@ -3224,19 +3227,25 @@
 
   //
   // Raw Monitor functions
   //
 
+// Tsan note: The JVMTI raw monitors are instrumented at JvmtiRawMonitor call
+// sites instead of inside the JvmtiRawMonitor implementation. This seems
+// cleaner, and mirrors instrumentation of JVM_RawMonitor* functions.
+
 // name - pre-checked for NULL
 // monitor_ptr - pre-checked for NULL
 jvmtiError
 JvmtiEnv::CreateRawMonitor(const char* name, jrawMonitorID* monitor_ptr) {
   JvmtiRawMonitor* rmonitor = new JvmtiRawMonitor(name);
   NULL_CHECK(rmonitor, JVMTI_ERROR_OUT_OF_MEMORY);
 
   *monitor_ptr = (jrawMonitorID)rmonitor;
 
+  TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_CREATE(rmonitor));
+
   return JVMTI_ERROR_NONE;
 } /* end CreateRawMonitor */
 
 
 // rmonitor - pre-checked for validity
@@ -3255,10 +3264,11 @@
       // failure on systems that don't like destroying synchronization
       // objects that are locked.
       int r;
       int recursion = rmonitor->recursions();
       for (int i = 0; i <= recursion; i++) {
+        TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_RELEASED(rmonitor));
         r = rmonitor->raw_exit(thread);
         assert(r == JvmtiRawMonitor::M_OK, "raw_exit should have worked");
         if (r != JvmtiRawMonitor::M_OK) {  // robustness
           return JVMTI_ERROR_INTERNAL;
         }
@@ -3273,10 +3283,11 @@
       // monitor's memory).
       return JVMTI_ERROR_NOT_MONITOR_OWNER;
     }
   }
 
+  TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_DESTROY(rmonitor));
   delete rmonitor;
 
   return JVMTI_ERROR_NONE;
 } /* end DestroyRawMonitor */
 
@@ -3316,10 +3327,11 @@
       // restore state, still at a safepoint safe state
       current_thread->set_thread_state(state);
     } else {
       rmonitor->raw_enter(thread);
     }
+    TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_ACQUIRED(rmonitor));
   }
   return JVMTI_ERROR_NONE;
 } /* end RawMonitorEnter */
 
 
@@ -3333,10 +3345,11 @@
     // Bool value from exit is false if rmonitor is not in the list.
     if (!JvmtiPendingMonitors::exit(rmonitor)) {
       err = JVMTI_ERROR_NOT_MONITOR_OWNER;
     }
   } else {
+    Thread* thread = Thread::current();
     Thread* thread = Thread::current();
     int r = rmonitor->raw_exit(thread);
     if (r == JvmtiRawMonitor::M_ILLEGAL_MONITOR_STATE) {
       err = JVMTI_ERROR_NOT_MONITOR_OWNER;
     }
@@ -3346,11 +3359,17 @@
 
 
 // rmonitor - pre-checked for validity
 jvmtiError
 JvmtiEnv::RawMonitorWait(JvmtiRawMonitor * rmonitor, jlong millis) {
+  Thread* thread = Thread::current();
+
+  // A wait is modeled in Tsan as a simple release-acquire pair.
+  // The matching release annotation is below.
   Thread* thread = Thread::current();
+  int r = rmonitor->raw_wait(millis, thread);
+  // The matching acquire annotation is above.
   int r = rmonitor->raw_wait(millis, thread);
 
   switch (r) {
   case JvmtiRawMonitor::M_INTERRUPTED:
     return JVMTI_ERROR_INTERRUPT;
diff a/src/hotspot/share/prims/jvmtiRawMonitor.cpp b/src/hotspot/share/prims/jvmtiRawMonitor.cpp
--- a/src/hotspot/share/prims/jvmtiRawMonitor.cpp
+++ b/src/hotspot/share/prims/jvmtiRawMonitor.cpp
@@ -27,10 +27,13 @@
 #include "prims/jvmtiRawMonitor.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/interfaceSupport.inline.hpp"
 #include "runtime/orderAccess.hpp"
 #include "runtime/thread.inline.hpp"
+#if INCLUDE_TSAN
+#include "tsan/tsan.hpp"
+#endif  // INCLUDE_TSAN
 
 JvmtiRawMonitor::QNode::QNode(Thread* thread) : _next(NULL), _prev(NULL),
                                                 _event(thread->_ParkEvent),
                                                 _notified(0), _t_state(TS_RUN) {
 }
@@ -45,10 +48,11 @@
   JavaThread* current_java_thread = JavaThread::current();
   assert(current_java_thread->thread_state() == _thread_in_vm, "Must be in vm");
   for (int i = 0; i < count(); i++) {
     JvmtiRawMonitor* rmonitor = monitors()->at(i);
     rmonitor->raw_enter(current_java_thread);
+    TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_ACQUIRED(rmonitor));
   }
   // pending monitors are converted to real monitor so delete them all.
   dispose();
 }
 
diff a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp
+++ b/src/hotspot/share/prims/jvmtiTagMap.cpp
@@ -57,10 +57,13 @@
 #include "runtime/thread.inline.hpp"
 #include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vmOperations.hpp"
+#if INCLUDE_TSAN
+#include "tsan/tsan.hpp"
+#endif  // INCLUDE_TSAN
 #include "utilities/macros.hpp"
 #if INCLUDE_ZGC
 #include "gc/z/zGlobals.hpp"
 #endif
 
@@ -375,10 +378,26 @@
 
   // iterate over all entries in the hashmap
   void entry_iterate(JvmtiTagHashmapEntryClosure* closure);
 };
 
+// Tsan should know that the JVMTI TagMap is protected by a mutex.
+class TsanMutexScope : public StackObj {
+ private:
+  Mutex *_lock;  // Keep my own reference, for destructor.
+
+ public:
+  // Don't actually lock it, just tell tsan we did.
+  TsanMutexScope(Mutex* mutex) : _lock(mutex) {
+    TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_ACQUIRED(_lock));
+  }
+
+  ~TsanMutexScope() {
+    TSAN_RUNTIME_ONLY(TSAN_RAW_LOCK_RELEASED(_lock));
+  }
+};
+
 // possible hashmap sizes - odd primes that roughly double in size.
 // To avoid excessive resizing the odd primes from 4801-76831 and
 // 76831-307261 have been removed. The list must be terminated by -1.
 int JvmtiTagHashmap::_sizes[] =  { 4801, 76831, 307261, 614563, 1228891,
     2457733, 4915219, 9830479, 19660831, 39321619, 78643219, -1 };
@@ -443,20 +462,37 @@
   _free_entries_count(0)
 {
   assert(JvmtiThreadState_lock->is_locked(), "sanity check");
   assert(((JvmtiEnvBase *)env)->tag_map() == NULL, "tag map already exists for environment");
 
+  // TSAN Note: we cannot tell TSAN about the creation of this lock due to
+  // this being seen as racy though is not really.
+  //
+  // The JvmtiTagMap gets created by the first thread to call tag_map_for; which
+  // uses a lock to create it if need be.
+  //
+  // This means that this lock is created under a mutex but then,
+  // subsequent uses do not have a lock to protect it (because not
+  // needed in this case), however TSAN sees it as being needed because:
+  //  - Another thread can come and get the newly created JvmtiTagMap without a
+  //  lock and acquire the lock.
+  //  - This provokes a race for TSAN on the lock itself, though there is no
+  //  real issue.
+  //
+  //  Not creating the lock or having a fence mechanism to tell TSAN this is
+  //  safe (a fake lock around this lock for example) seem to be the only
+  //  solutions.
+
   _hashmap = new JvmtiTagHashmap();
 
   // finally add us to the environment
   ((JvmtiEnvBase *)env)->release_set_tag_map(this);
 }
 
 
 // destroy a JvmtiTagMap
 JvmtiTagMap::~JvmtiTagMap() {
-
   // no lock acquired as we assume the enclosing environment is
   // also being destroryed.
   ((JvmtiEnvBase *)_env)->set_tag_map(NULL);
 
   JvmtiTagHashmapEntry** table = _hashmap->table();
@@ -479,10 +515,12 @@
     JvmtiTagHashmapEntry* next = entry->next();
     delete entry;
     entry = next;
   }
   _free_entries = NULL;
+
+  // TSAN Note: see above for the Tsan creation note.
 }
 
 // create a hashmap entry
 // - if there's an entry on the (per-environment) free list then this
 // is returned. Otherwise an new entry is allocated.
@@ -730,10 +768,11 @@
 // This function is performance critical. If many threads attempt to tag objects
 // around the same time then it's possible that the Mutex associated with the
 // tag map will be a hot lock.
 void JvmtiTagMap::set_tag(jobject object, jlong tag) {
   MutexLocker ml(lock());
+  TSAN_ONLY(TsanMutexScope tms(lock()));
 
   // resolve the object
   oop o = JNIHandles::resolve_non_null(object);
 
   // see if the object is already tagged
@@ -762,10 +801,11 @@
 }
 
 // get the tag for an object
 jlong JvmtiTagMap::get_tag(jobject object) {
   MutexLocker ml(lock());
+  TSAN_ONLY(TsanMutexScope tms(lock()));
 
   // resolve the object
   oop o = JNIHandles::resolve_non_null(object);
 
   return tag_for(this, o);
@@ -1261,15 +1301,26 @@
 // VM operation to iterate over all objects in the heap (both reachable
 // and unreachable)
 class VM_HeapIterateOperation: public VM_Operation {
  private:
   ObjectClosure* _blk;
+  JvmtiTagMap* _tag_map;
+
  public:
-  VM_HeapIterateOperation(ObjectClosure* blk) { _blk = blk; }
+  VM_HeapIterateOperation(ObjectClosure* blk, JvmtiTagMap* tag_map) {
+    _blk = blk;
+    _tag_map = tag_map;
+  }
 
   VMOp_Type type() const { return VMOp_HeapIterateOperation; }
   void doit() {
+    // Simulates barrier synchronization on safepoint.
+    // This annotation is reasonably minimal in number of tsan callbacks.
+    // By passing the lock directly, we are not actually locking it, just
+    // telling TSAN we are to "simulate" the lock.
+    TSAN_ONLY(TsanMutexScope tms(_tag_map->lock()));
+
     // allows class files maps to be cached during iteration
     ClassFieldMapCacheMark cm;
 
     // make sure that heap is parsable (fills TLABs with filler objects)
     Universe::heap()->ensure_parsability(false);  // no need to retire TLABs
@@ -1492,11 +1543,11 @@
   IterateOverHeapObjectClosure blk(this,
                                    klass,
                                    object_filter,
                                    heap_object_callback,
                                    user_data);
-  VM_HeapIterateOperation op(&blk);
+  VM_HeapIterateOperation op(&blk, this);
   VMThread::execute(&op);
 }
 
 
 // Iterates over all objects in the heap
@@ -1509,11 +1560,11 @@
   IterateThroughHeapObjectClosure blk(this,
                                       klass,
                                       heap_filter,
                                       callbacks,
                                       user_data);
-  VM_HeapIterateOperation op(&blk);
+  VM_HeapIterateOperation op(&blk, this);
   VMThread::execute(&op);
 }
 
 // support class for get_objects_with_tags
 
@@ -1605,10 +1656,11 @@
 
   TagObjectCollector collector(env(), tags, count);
   {
     // iterate over all tagged objects
     MutexLocker ml(lock());
+    TSAN_ONLY(TsanMutexScope tms(lock()));
     entry_iterate(&collector);
   }
   return collector.result(count_ptr, object_result_ptr, tag_result_ptr);
 }
 
@@ -3220,10 +3272,15 @@
 
   return true;
 }
 
 void VM_HeapWalkOperation::doit() {
+  // This annotation is reasonably minimal in number of tsan callbacks.
+  // By passing the lock directly, we are not actually locking it, just
+  // telling TSAN we are to "simulate" the lock.
+  TSAN_ONLY(TsanMutexScope tms(_tag_map->lock()));
+
   ResourceMark rm;
   ObjectMarkerController marker;
   ClassFieldMapCacheMark cm;
 
   assert(visit_stack()->is_empty(), "visit stack must be empty");
diff a/src/hotspot/share/prims/jvmtiTagMap.hpp b/src/hotspot/share/prims/jvmtiTagMap.hpp
--- a/src/hotspot/share/prims/jvmtiTagMap.hpp
+++ b/src/hotspot/share/prims/jvmtiTagMap.hpp
@@ -53,20 +53,21 @@
 
   // create a tag map
   JvmtiTagMap(JvmtiEnv* env);
 
   // accessors
-  inline Mutex* lock()                      { return &_lock; }
   inline JvmtiEnv* env() const              { return _env; }
 
   void do_weak_oops(BoolObjectClosure* is_alive, OopClosure* f);
 
   // iterate over all entries in this tag map
   void entry_iterate(JvmtiTagHashmapEntryClosure* closure);
 
  public:
 
+  inline Mutex* lock()                      { return &_lock; }
+
   // indicates if this tag map is locked
   bool is_locked()                          { return lock()->is_locked(); }
 
   JvmtiTagHashmap* hashmap() { return _hashmap; }
 
diff a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp
--- a/src/hotspot/share/prims/unsafe.cpp
+++ b/src/hotspot/share/prims/unsafe.cpp
@@ -50,10 +50,13 @@
 #include "services/threadService.hpp"
 #include "utilities/align.hpp"
 #include "utilities/copy.hpp"
 #include "utilities/dtrace.hpp"
 #include "utilities/macros.hpp"
+#if INCLUDE_TSAN
+#include "tsan/tsanExternalDecls.hpp"
+#endif
 
 /**
  * Implementation of the jdk.internal.misc.Unsafe class
  */
 
@@ -261,69 +264,111 @@
 // That is, it should be in the range [0, MAX_OBJECT_SIZE].
 UNSAFE_ENTRY(jobject, Unsafe_GetReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) {
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
   oop v = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_load_at(p, offset);
+  TSAN_RUNTIME_ONLY(
+    void* addr = index_oop_from_field_offset_long(p, offset);
+    if (UseCompressedOops) {
+      __tsan_read4_pc(addr, SharedRuntime::tsan_code_location(0, 0));
+    } else {
+      __tsan_read8_pc(addr, SharedRuntime::tsan_code_location(0, 0));
+    }
+  );
   return JNIHandles::make_local(env, v);
 } UNSAFE_END
 
 UNSAFE_ENTRY(void, Unsafe_PutReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) {
   oop x = JNIHandles::resolve(x_h);
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
+  TSAN_RUNTIME_ONLY(
+    void* addr = index_oop_from_field_offset_long(p, offset);
+    if (UseCompressedOops) {
+      __tsan_write4_pc(addr, SharedRuntime::tsan_code_location(0, 0));
+    } else {
+      __tsan_write8_pc(addr, SharedRuntime::tsan_code_location(0, 0));
+    }
+  );
   HeapAccess<ON_UNKNOWN_OOP_REF>::oop_store_at(p, offset, x);
 } UNSAFE_END
 
 UNSAFE_ENTRY(jobject, Unsafe_GetReferenceVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) {
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
   oop v = HeapAccess<MO_SEQ_CST | ON_UNKNOWN_OOP_REF>::oop_load_at(p, offset);
+  TSAN_RUNTIME_ONLY(
+    void* addr = index_oop_from_field_offset_long(p, offset);
+    __tsan_java_acquire(addr);
+  );
   return JNIHandles::make_local(env, v);
 } UNSAFE_END
 
 UNSAFE_ENTRY(void, Unsafe_PutReferenceVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) {
   oop x = JNIHandles::resolve(x_h);
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
+  TSAN_RUNTIME_ONLY(
+    void* addr = index_oop_from_field_offset_long(p, offset);
+    __tsan_java_release(addr);
+  );
   HeapAccess<MO_SEQ_CST | ON_UNKNOWN_OOP_REF>::oop_store_at(p, offset, x);
 } UNSAFE_END
 
 UNSAFE_ENTRY(jobject, Unsafe_GetUncompressedObject(JNIEnv *env, jobject unsafe, jlong addr)) {
   oop v = *(oop*) (address) addr;
   return JNIHandles::make_local(env, v);
 } UNSAFE_END
 
-#define DEFINE_GETSETOOP(java_type, Type) \
+#define DEFINE_GETSETOOP(java_type, Type, size) \
  \
 UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
-  return MemoryAccess<java_type>(thread, obj, offset).get(); \
+  java_type ret = MemoryAccess<java_type>(thread, obj, offset).get(); \
+  TSAN_RUNTIME_ONLY( \
+    void* addr = index_oop_from_field_offset_long(JNIHandles::resolve(obj), offset); \
+    __tsan_read##size##_pc(addr, SharedRuntime::tsan_code_location(0, 0)); \
+  ); \
+  return ret; \
 } UNSAFE_END \
  \
 UNSAFE_ENTRY(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
+  TSAN_RUNTIME_ONLY( \
+    void* addr = index_oop_from_field_offset_long(JNIHandles::resolve(obj), offset); \
+    __tsan_write##size##_pc(addr, SharedRuntime::tsan_code_location(0, 0)); \
+  ); \
   MemoryAccess<java_type>(thread, obj, offset).put(x); \
 } UNSAFE_END \
  \
 // END DEFINE_GETSETOOP.
 
-DEFINE_GETSETOOP(jboolean, Boolean)
-DEFINE_GETSETOOP(jbyte, Byte)
-DEFINE_GETSETOOP(jshort, Short);
-DEFINE_GETSETOOP(jchar, Char);
-DEFINE_GETSETOOP(jint, Int);
-DEFINE_GETSETOOP(jlong, Long);
-DEFINE_GETSETOOP(jfloat, Float);
-DEFINE_GETSETOOP(jdouble, Double);
+DEFINE_GETSETOOP(jboolean, Boolean, 1)
+DEFINE_GETSETOOP(jbyte, Byte, 1)
+DEFINE_GETSETOOP(jshort, Short, 2);
+DEFINE_GETSETOOP(jchar, Char, 2);
+DEFINE_GETSETOOP(jint, Int, 4);
+DEFINE_GETSETOOP(jlong, Long, 8);
+DEFINE_GETSETOOP(jfloat, Float, 4);
+DEFINE_GETSETOOP(jdouble, Double, 8);
 
 #undef DEFINE_GETSETOOP
 
 #define DEFINE_GETSETOOP_VOLATILE(java_type, Type) \
  \
 UNSAFE_ENTRY(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
-  return MemoryAccess<java_type>(thread, obj, offset).get_volatile(); \
+  java_type ret = MemoryAccess<java_type>(thread, obj, offset).get_volatile(); \
+  TSAN_RUNTIME_ONLY( \
+    void* addr = index_oop_from_field_offset_long(JNIHandles::resolve(obj), offset); \
+    __tsan_java_acquire(addr); \
+  ); \
+  return ret; \
 } UNSAFE_END \
  \
 UNSAFE_ENTRY(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
+  TSAN_RUNTIME_ONLY( \
+    void* addr = index_oop_from_field_offset_long(JNIHandles::resolve(obj), offset); \
+    __tsan_java_release(addr); \
+  ); \
   MemoryAccess<java_type>(thread, obj, offset).put_volatile(x); \
 } UNSAFE_END \
  \
 // END DEFINE_GETSETOOP_VOLATILE.
 
@@ -897,68 +942,102 @@
   env->Throw(thr);
 } UNSAFE_END
 
 // JSR166 ------------------------------------------------------------------
 
+// Calls __tsan_java_release() on construct and __tsan_java_acquire() on destruct.
+class ScopedReleaseAcquire: public StackObj {
+private:
+  void* _addr;
+public:
+  ScopedReleaseAcquire(volatile void* addr) {
+    TSAN_RUNTIME_ONLY(
+      _addr = const_cast<void*>(addr);
+      __tsan_java_release(_addr);
+    );
+  }
+
+  ScopedReleaseAcquire(oop obj, jlong offset) {
+    TSAN_RUNTIME_ONLY(
+      _addr = index_oop_from_field_offset_long(obj, offset);
+      __tsan_java_release(_addr);
+    );
+  }
+
+  ~ScopedReleaseAcquire() {
+    TSAN_RUNTIME_ONLY(__tsan_java_acquire(_addr));
+  }
+};
+
 UNSAFE_ENTRY(jobject, Unsafe_CompareAndExchangeReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h)) {
   oop x = JNIHandles::resolve(x_h);
   oop e = JNIHandles::resolve(e_h);
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
+  ScopedReleaseAcquire releaseAcquire(p, offset);
   oop res = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x);
   return JNIHandles::make_local(env, res);
 } UNSAFE_END
 
 UNSAFE_ENTRY(jint, Unsafe_CompareAndExchangeInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
   oop p = JNIHandles::resolve(obj);
   if (p == NULL) {
     volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset);
+    ScopedReleaseAcquire releaseAcquire(addr);
     return RawAccess<>::atomic_cmpxchg(addr, e, x);
   } else {
     assert_field_offset_sane(p, offset);
+    ScopedReleaseAcquire releaseAcquire(p, offset);
     return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x);
   }
 } UNSAFE_END
 
 UNSAFE_ENTRY(jlong, Unsafe_CompareAndExchangeLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) {
   oop p = JNIHandles::resolve(obj);
   if (p == NULL) {
     volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset);
+    ScopedReleaseAcquire releaseAcquire(addr);
     return RawAccess<>::atomic_cmpxchg(addr, e, x);
   } else {
     assert_field_offset_sane(p, offset);
+    ScopedReleaseAcquire releaseAcquire(p, offset);
     return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x);
   }
 } UNSAFE_END
 
 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetReference(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject e_h, jobject x_h)) {
   oop x = JNIHandles::resolve(x_h);
   oop e = JNIHandles::resolve(e_h);
   oop p = JNIHandles::resolve(obj);
   assert_field_offset_sane(p, offset);
+  ScopedReleaseAcquire releaseAcquire(p, offset);
   oop ret = HeapAccess<ON_UNKNOWN_OOP_REF>::oop_atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x);
   return ret == e;
 } UNSAFE_END
 
 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
   oop p = JNIHandles::resolve(obj);
   if (p == NULL) {
     volatile jint* addr = (volatile jint*)index_oop_from_field_offset_long(p, offset);
+    ScopedReleaseAcquire releaseAcquire(addr);
     return RawAccess<>::atomic_cmpxchg(addr, e, x) == e;
   } else {
     assert_field_offset_sane(p, offset);
+    ScopedReleaseAcquire releaseAcquire(p, offset);
     return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x) == e;
   }
 } UNSAFE_END
 
 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x)) {
   oop p = JNIHandles::resolve(obj);
   if (p == NULL) {
     volatile jlong* addr = (volatile jlong*)index_oop_from_field_offset_long(p, offset);
+    ScopedReleaseAcquire releaseAcquire(addr);
     return RawAccess<>::atomic_cmpxchg(addr, e, x) == e;
   } else {
     assert_field_offset_sane(p, offset);
+    ScopedReleaseAcquire releaseAcquire(p, offset);
     return HeapAccess<>::atomic_cmpxchg_at(p, (ptrdiff_t)offset, e, x) == e;
   }
 } UNSAFE_END
 
 static void post_thread_park_event(EventThreadPark* event, const oop obj, jlong timeout_nanos, jlong until_epoch_millis) {
diff a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -4039,10 +4039,22 @@
   if (FLAG_IS_CMDLINE(CompilationMode)) {
     warning("CompilationMode has no effect in non-tiered VMs");
   }
 #endif
 
+  TSAN_RUNTIME_ONLY(
+    // Currently TSAN is only implemented for interpreter.
+    set_mode_flags(_int);
+    // TSAN instrumentation is not implemented for the RewriteBytecodes
+    // code paths because TSAN slows down the application so much that the
+    // performance benefits from rewriting bytecodes is negligible.
+    FLAG_SET_ERGO(RewriteBytecodes, false);
+    FLAG_SET_ERGO(RewriteFrequentPairs, false);
+    // Turn off CDS, it interferes with eagerly allocating jmethodIDs.
+    no_shared_spaces("CDS is not compatible with TSAN");
+  );
+
   return JNI_OK;
 }
 
 jint Arguments::apply_ergo() {
   // Set flags based on ergonomics.
diff a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2491,10 +2491,20 @@
                "(Deprecated) Use new algorithm to compute field layouts")   \
                                                                             \
   product(bool, UseEmptySlotsInSupers, true,                                \
                 "Allow allocating fields in empty slots of super-classes")  \
                                                                             \
+  TSAN_ONLY(product(bool, ThreadSanitizer, false,                           \
+          "Enable ThreadSanitizer lock instrumentation"))                   \
+                                                                            \
+  TSAN_ONLY(product(bool, ThreadSanitizerJavaMemory, true,                  \
+          "Detect Java data races with ThreadSanitizer. "                   \
+          "This is only enabled if -XX:+ThreadSanitizer is set."))          \
+                                                                            \
+  TSAN_ONLY(product(ccstr, ThreadSanitizerIgnoreFile, NULL,                 \
+          "File containing a list of ignored field patterns for "           \
+          "ThreadSanitizer."))                                              \
 
 
 // Interface macros
 #define DECLARE_PRODUCT_FLAG(type, name, value, doc)      extern "C" type name;
 #define DECLARE_PD_PRODUCT_FLAG(type, name, doc)          extern "C" type name;
diff a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp
--- a/src/hotspot/share/runtime/init.cpp
+++ b/src/hotspot/share/runtime/init.cpp
@@ -63,10 +63,11 @@
 void codeCache_init();
 void VM_Version_init();
 void stubRoutines_init1();
 jint universe_init();          // depends on codeCache_init and stubRoutines_init
 // depends on universe_init, must be before interpreter_init (currently only on SPARC)
+TSAN_ONLY(jint tsan_init();)
 void gc_barrier_stubs_init();
 void interpreter_init();       // before any methods loaded
 void invocationCounter_init(); // before any methods loaded
 void accessFlags_init();
 void templateTable_init();
@@ -90,10 +91,11 @@
 // Do not disable thread-local-storage, as it is important for some
 // JNI/JVM/JVMTI functions and signal handlers to work properly
 // during VM shutdown
 void perfMemory_exit();
 void ostream_exit();
+TSAN_ONLY(void tsan_exit();)
 
 void vm_init_globals() {
   check_ThreadShadow();
   basic_types_init();
   eventlog_init();
@@ -102,11 +104,10 @@
   chunkpool_init();
   perfMemory_init();
   SuspendibleThreadSet_init();
 }
 
-
 jint init_globals() {
   HandleMark hm;
   management_init();
   bytecodes_init();
   classLoader_init1();
@@ -117,10 +118,17 @@
   jint status = universe_init();  // dependent on codeCache_init and
                                   // stubRoutines_init1 and metaspace_init.
   if (status != JNI_OK)
     return status;
 
+  TSAN_RUNTIME_ONLY(
+    status = tsan_init();
+    if (status != JNI_OK) {
+      return status;
+    }
+  );
+
   gc_barrier_stubs_init();   // depends on universe_init, must be before interpreter_init
   interpreter_init();        // before any methods loaded
   invocationCounter_init();  // before any methods loaded
   accessFlags_init();
   templateTable_init();
@@ -173,10 +181,13 @@
 
 void exit_globals() {
   static bool destructorsCalled = false;
   if (!destructorsCalled) {
     destructorsCalled = true;
+
+    TSAN_RUNTIME_ONLY(tsan_exit());
+
     if (log_is_enabled(Info, monitorinflation)) {
       // The ObjectMonitor subsystem uses perf counters so
       // do this before perfMemory_exit().
       // ObjectSynchronizer::finish_deflate_idle_monitors()'s call
       // to audit_and_print_stats() is done at the Debug level.
diff a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -126,10 +126,14 @@
 Mutex*   JfrBuffer_lock               = NULL;
 Mutex*   JfrStream_lock               = NULL;
 Monitor* JfrThreadSampler_lock        = NULL;
 #endif
 
+#if INCLUDE_TSAN
+Mutex*   TsanOopMap_lock              = NULL;
+#endif
+
 #ifndef SUPPORTS_NATIVE_CX8
 Mutex*   UnsafeJlong_lock             = NULL;
 #endif
 Mutex*   CodeHeapStateAnalytics_lock  = NULL;
 
@@ -317,10 +321,14 @@
   def(JfrStream_lock               , PaddedMutex  , nonleaf + 1, false, _safepoint_check_never);
   def(JfrStacktrace_lock           , PaddedMutex  , special,     true,  _safepoint_check_never);
   def(JfrThreadSampler_lock        , PaddedMonitor, leaf,        true,  _safepoint_check_never);
 #endif
 
+  TSAN_RUNTIME_ONLY(
+    def(TsanOopMap_lock            , PaddedMutex  , special,     true,  _safepoint_check_never);
+  );
+
 #ifndef SUPPORTS_NATIVE_CX8
   def(UnsafeJlong_lock             , PaddedMutex  , special,     false, _safepoint_check_never);
 #endif
 
   def(CodeHeapStateAnalytics_lock  , PaddedMutex  , leaf,        true,  _safepoint_check_never);
diff a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -132,10 +132,13 @@
 extern Monitor* JfrMsg_lock;                     // protects JFR messaging
 extern Mutex*   JfrBuffer_lock;                  // protects JFR buffer operations
 extern Mutex*   JfrStream_lock;                  // protects JFR stream access
 extern Monitor* JfrThreadSampler_lock;           // used to suspend/resume JFR thread sampler
 #endif
+#if INCLUDE_TSAN
+extern Mutex*   TsanOopMap_lock;                 // guards shared map of oops
+#endif
 
 #ifndef SUPPORTS_NATIVE_CX8
 extern Mutex*   UnsafeJlong_lock;                // provides Unsafe atomic updates to jlongs on platforms that don't support cx8
 #endif
 
diff a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -74,10 +74,14 @@
 #include "utilities/macros.hpp"
 #include "utilities/xmlstream.hpp"
 #ifdef COMPILER1
 #include "c1/c1_Runtime1.hpp"
 #endif
+#if INCLUDE_TSAN
+#include "tsan/tsanExternalDecls.hpp"
+#include "tsan/tsanOopMap.hpp"
+#endif
 
 // Shared stub locations
 RuntimeStub*        SharedRuntime::_wrong_method_blob;
 RuntimeStub*        SharedRuntime::_wrong_method_abstract_blob;
 RuntimeStub*        SharedRuntime::_ic_miss_blob;
@@ -1006,10 +1010,180 @@
       (char *) name->bytes(), name->utf8_length(),
       (char *) sig->bytes(), sig->utf8_length());
   return 0;
 JRT_END
 
+#if INCLUDE_TSAN
+
+JRT_LEAF(void, SharedRuntime::verify_oop_index(oopDesc* obj, int index))
+  assert(oopDesc::is_oop(obj), "invalid oop");
+  assert(index >= 0, "index is less than 0");
+  int obj_size_in_bytes = obj->size() * HeapWordSize;
+  assert(index < obj_size_in_bytes, "index %d >= obj size %d", index, obj_size_in_bytes);
+JRT_END
+
+// TSAN: method entry callback from interpreter
+// (1) In order to have the line numbers in the call stack, we use the caller
+//     address instead of the method that's being called. This also matches
+//     the entry/exit convention that TSAN uses for C++.
+// We use JRT_ENTRY since call_VM_leaf doesn't set _last_Java_sp that we need.
+JRT_ENTRY(void, SharedRuntime::tsan_interp_method_entry(JavaThread *thread))
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  DEBUG_ONLY(NoHandleMark nhm;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+
+  RegisterMap unused_reg_map(thread, false);
+
+  // These asserts should be removed once
+  // we support more than just the interpreter for TSAN.
+  assert(!thread->last_frame().is_compiled_frame(),
+         "Current frame should not be a compiled frame");
+  const frame sender = thread->last_frame().real_sender(&unused_reg_map);
+  assert(!sender.is_compiled_frame(), "Sender should not be a compiled frame");
+
+  jmethodID jmethod_id = 0;
+  u2 bci = 0;
+  // TODO: is (0, 0) really the best we can do
+  // when the sender isn't an interpreted frame?
+  if (sender.is_interpreted_frame()) {
+    jmethod_id = sender.interpreter_frame_method()->find_jmethod_id_or_null();
+    bci = sender.interpreter_frame_bci();
+  }
+  __tsan_func_entry(tsan_code_location(jmethod_id, bci));
+JRT_END
+
+// TSAN: method exit callback from interpreter
+JRT_LEAF(void, SharedRuntime::tsan_interp_method_exit())
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  __tsan_func_exit();
+JRT_END
+
+void SharedRuntime::tsan_oop_lock(Thread* thread, oop obj) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+
+  TsanOopMap::add_oop(obj);
+  __tsan_java_mutex_lock((julong)(oopDesc*)obj);
+}
+
+void SharedRuntime::tsan_oop_unlock(Thread *thread, oop obj) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+  assert(TsanOopMap::exists(obj), "oop seen in unlock but not tracked");
+
+  __tsan_java_mutex_unlock((julong)(oopDesc*)obj);
+}
+
+void SharedRuntime::tsan_oop_rec_lock(Thread* thread, oop obj, int rec) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+
+  TsanOopMap::add_oop(obj);
+  __tsan_java_mutex_lock_rec((julong)(oopDesc*)obj, rec);
+}
+
+int SharedRuntime::tsan_oop_rec_unlock(Thread *thread, oop obj) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(thread != NULL, "null thread");
+  assert(obj != NULL, "null oop");
+  assert(oopDesc::is_oop(obj), "invalid oop");
+  assert(TsanOopMap::exists(obj), "oop seen in unlock but not tracked");
+
+  return __tsan_java_mutex_unlock_rec((julong)(oopDesc*)obj);
+}
+
+JRT_LEAF(void, SharedRuntime::tsan_interp_lock(JavaThread* thread,
+                                               BasicObjectLock* elem))
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+  assert(elem != NULL, "null elem");
+
+  oop obj = elem->obj();
+  tsan_oop_lock(thread, obj);
+
+  assert(obj == elem->obj(), "oop changed");
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+JRT_END
+
+JRT_LEAF(void, SharedRuntime::tsan_interp_unlock(JavaThread* thread,
+                                                 BasicObjectLock* elem))
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+  assert(elem != NULL, "null elem");
+
+  oop obj = elem->obj();
+  tsan_oop_unlock(thread, obj);
+
+  assert(obj == elem->obj(), "oop changed");
+  DEBUG_ONLY(thread->last_frame().interpreter_frame_verify_monitor(elem);)
+JRT_END
+
+// Should be JRT_LEAF, but this is called very early during VM startup, so we
+// are sometimes in '_thread_in_vm' state.
+// NOTE: DO NOT add operations that can safepoint, enter GC, or throw an
+// exception!
+void SharedRuntime::tsan_track_obj_with_size(oopDesc* obj, int size) {
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(oopDesc::is_oop(obj), "Bad oopDesc passed to tsan_track_obj_with_size().");
+  TsanOopMap::add_oop_with_size(obj, size);
+}
+
+JRT_LEAF(void, SharedRuntime::tsan_track_obj(oopDesc* obj))
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(oopDesc::is_oop(obj), "Bad oopDesc passed to tsan_track_obj().");
+  TsanOopMap::add_oop(obj);
+JRT_END
+
+// TODO: Make tsan_acquire/release JRT_LEAF
+// Currently it can't be JRT_LEAF because there are calls from the VM
+// (instanceKlass.cpp), and JRT_LEAF only allows calls from Java/native code.
+// We need to figure out a better way of being able to call TSAN functions from
+// the VM.
+void SharedRuntime::tsan_acquire(void* address) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(address != NULL, "Cannot acquire at address 0");
+  __tsan_java_acquire(address);
+}
+
+void SharedRuntime::tsan_release(void* address) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+  assert(address != NULL, "Cannot release at address 0");
+  __tsan_java_release(address);
+}
+
+#define TSAN_MEMORY_ACCESS(name)                                               \
+  JRT_LEAF(void, SharedRuntime::tsan_##name(                                   \
+      void* addr,                                                              \
+      Method* method,                                                          \
+      address bcp))                                                            \
+    assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");                      \
+    assert(ThreadSanitizerJavaMemory, "Need -XX:+ThreadSanitizerJavaMemory");  \
+    jmethodID mid = method->find_jmethod_id_or_null();                         \
+    int bci = method->bci_from(bcp);                                           \
+    __tsan_##name##_pc(addr, tsan_code_location(mid, bci));                    \
+  JRT_END
+
+TSAN_MEMORY_ACCESS(read1)
+TSAN_MEMORY_ACCESS(read2)
+TSAN_MEMORY_ACCESS(read4)
+TSAN_MEMORY_ACCESS(read8)
+TSAN_MEMORY_ACCESS(write1)
+TSAN_MEMORY_ACCESS(write2)
+TSAN_MEMORY_ACCESS(write4)
+TSAN_MEMORY_ACCESS(write8)
+
+#endif // INCLUDE_TSAN
 
 // Finds receiver, CallInfo (i.e. receiver method), and calling bytecode)
 // for a call current in progress, i.e., arguments has been pushed on stack
 // put callee has not been invoked yet.  Used by: resolve virtual/static,
 // vtable updates, etc.  Caller frame must be compiled.
diff a/src/hotspot/share/runtime/sharedRuntime.hpp b/src/hotspot/share/runtime/sharedRuntime.hpp
--- a/src/hotspot/share/runtime/sharedRuntime.hpp
+++ b/src/hotspot/share/runtime/sharedRuntime.hpp
@@ -276,10 +276,87 @@
   static int dtrace_object_alloc(oopDesc* o, int size);
   static int dtrace_object_alloc_base(Thread* thread, oopDesc* o, int size);
   static int dtrace_method_entry(JavaThread* thread, Method* m);
   static int dtrace_method_exit(JavaThread* thread, Method* m);
 
+#if INCLUDE_TSAN
+  // TSAN instrumentation
+
+  // TSAN uses a 64-bit value to identify code location.
+  // TSAN uses the uppermost 3 bits (63:61) for the internal purposes.
+  // If bit 60 is set, TSAN recognizes that the code location belongs to the
+  // JVM, and will call __tsan_symbolize_external_ex() for symbolization rather
+  // than TSAN's own symbolizer. See __sanitizer::kExternalPCBit and
+  // __tsan::__tsan_symbolize_external_ex() in TSAN for more details.
+  // The lower 60 bits may contain either a packed bytecode location, or an
+  // instruction address inside the code generated by JIT compiler.
+  // A packed code location has the method ID in bits 59:16 and the bytecode
+  //offset within method in bits 15:0. 44 bits (59:16) are enough to encode any
+  // 47-bit 8-byte-aligned address, which is the maximum address space TSAN
+  // allows. The next 16 bits are used for storing the bci.
+  // | Tsan: 3 | TsanJava: 1 | jmethodID: 44 | BCI: 16 |
+  static const int tsan_method_id_alignment_bits = 3;
+  static const int tsan_bci_bits = 16;
+  static const u8 tsan_bci_mask = right_n_bits(tsan_bci_bits);
+  static const int tsan_method_id_shift = tsan_bci_bits -
+      tsan_method_id_alignment_bits;
+  static const u8 tsan_fake_pc_bit = 1L << 60;
+  static void * tsan_code_location(jmethodID jmethod_id_ptr, u2 bci) {
+    return (void *)(tsan_fake_pc_bit |
+      (((u8)(jmethod_id_ptr)) << tsan_method_id_shift) | bci);
+  }
+  static jmethodID tsan_method_id_from_code_location(u8 loc) {
+    return (jmethodID)(
+        (loc & ~(tsan_fake_pc_bit | tsan_bci_mask)) >> tsan_method_id_shift);
+  }
+  static u2 tsan_bci_from_code_location(u8 loc) {
+    return (u2)(loc & tsan_bci_mask);
+  }
+
+  // These functions are wrappers around TSAN callbacks,
+  // which are listed in tsanExternalDecls.hpp. The VM uses only these
+  // functions to push events to ThreadSanitizer.
+
+  // Verify that an oop is valid and that the index is within the object size.
+  static void verify_oop_index(oopDesc* obj, int index);
+
+  // Java method entry/exit from code run by template interpreter
+  static void tsan_interp_method_entry(JavaThread *thread);
+  static void tsan_interp_method_exit();
+
+  // Monitor acquire/release in VM code
+  // (e.g., generated native method wrapper, JNI heavyweight locks)
+  static void tsan_oop_lock(Thread* thread, oop obj);
+  static void tsan_oop_unlock(Thread* thread, oop obj);
+  // Monitor acquire/release in VM code; recursive lock variant (e.g., wait())
+  static void tsan_oop_rec_lock(Thread* thread, oop obj, int rec);
+  static int tsan_oop_rec_unlock(Thread* thread, oop obj);
+
+  // Monitor acquire/release from code run by template interpreter
+  static void tsan_interp_lock(JavaThread* thread, BasicObjectLock* elem);
+  static void tsan_interp_unlock(JavaThread* thread, BasicObjectLock* elem);
+
+  // Address must point to an object in the Java heap.
+  static void tsan_acquire(void* address);
+  static void tsan_release(void* address);
+
+  // Called whenever an obj is created.
+  static void tsan_track_obj_with_size(oopDesc* obj, int size);
+  static void tsan_track_obj(oopDesc* obj);
+
+  // Memory reads/writes from code run by template interpreter
+  static void tsan_read1(void* addr, Method* method, address bcp);
+  static void tsan_read2(void* addr, Method* method, address bcp);
+  static void tsan_read4(void* addr, Method* method, address bcp);
+  static void tsan_read8(void* addr, Method* method, address bcp);
+  static void tsan_write1(void* addr, Method* method, address bcp);
+  static void tsan_write2(void* addr, Method* method, address bcp);
+  static void tsan_write4(void* addr, Method* method, address bcp);
+  static void tsan_write8(void* addr, Method* method, address bcp);
+
+#endif // INCLUDE_TSAN
+
   // Utility method for retrieving the Java thread id, returns 0 if the
   // thread is not a well formed Java thread.
   static jlong get_java_tid(Thread* thread);
 
 
diff a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp
--- a/src/hotspot/share/runtime/synchronizer.cpp
+++ b/src/hotspot/share/runtime/synchronizer.cpp
@@ -609,10 +609,18 @@
 //  2) wait on lock2
 //  3) when notified on lock2, unlock lock2
 //  4) reenter lock1 with original recursion count
 //  5) lock lock2
 // NOTE: must use heavy weight monitor to handle complete_exit/reenter()
+// NOTE(TSAN): We cannot instrument complete_exit/reenter in ObjectSynchronizer
+//             in a manner similar to wait and waitUninterruptibly, because
+//             (1) recursion count stored by inflated monitor is different from
+//             the absolute recursion count tracked by Tsan, and (2) in the
+//             general case, we cannot merely store Tsan's recursion count
+//             once: we must track it for *each invocation* of complete_exit.
+//             Hence, the best place to instrument for Tsan is at the call site
+//             for complete_exit/reenter. Luckily, there is only one call site.
 intx ObjectSynchronizer::complete_exit(Handle obj, TRAPS) {
   if (UseBiasedLocking) {
     BiasedLocking::revoke(obj, THREAD);
     assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
   }
@@ -643,10 +651,11 @@
     assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
   }
   THREAD->set_current_pending_monitor_is_from_java(false);
   inflate(THREAD, obj(), inflate_cause_jni_enter)->enter(THREAD);
   THREAD->set_current_pending_monitor_is_from_java(true);
+  TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_lock(THREAD, obj()));
 }
 
 // NOTE: must use heavy weight monitor to handle jni monitor exit
 void ObjectSynchronizer::jni_exit(oop obj, Thread* THREAD) {
   if (UseBiasedLocking) {
@@ -659,10 +668,11 @@
   ObjectMonitor* monitor = inflate(THREAD, obj, inflate_cause_jni_exit);
   // If this thread has locked the object, exit the monitor. We
   // intentionally do not use CHECK here because we must exit the
   // monitor even if an exception is pending.
   if (monitor->check_owner(THREAD)) {
+    TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_unlock(THREAD, obj));
     monitor->exit(true, THREAD);
   }
 }
 
 // -----------------------------------------------------------------------------
@@ -674,15 +684,17 @@
   _thread->check_for_valid_safepoint_state();
   _obj = obj;
 
   if (_dolock) {
     ObjectSynchronizer::enter(_obj, &_lock, _thread);
+    TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_lock(_thread, _obj()));
   }
 }
 
 ObjectLocker::~ObjectLocker() {
   if (_dolock) {
+    TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_unlock(_thread, _obj()));
     ObjectSynchronizer::exit(_obj(), &_lock, _thread);
   }
 }
 
 
@@ -698,12 +710,21 @@
     THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
   }
   ObjectMonitor* monitor = inflate(THREAD, obj(), inflate_cause_wait);
 
   DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), THREAD, millis);
+
+  TSAN_ONLY(int tsan_rec = 0;)
+  TSAN_RUNTIME_ONLY(
+    tsan_rec = SharedRuntime::tsan_oop_rec_unlock(THREAD, obj());
+    assert(tsan_rec > 0, "tsan: unlocking unlocked mutex");
+  );
+
   monitor->wait(millis, true, THREAD);
 
+  TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_rec_lock(THREAD, obj(), tsan_rec));
+
   // This dummy call is in place to get around dtrace bug 6254741.  Once
   // that's fixed we can uncomment the following line, remove the call
   // and change this function back into a "void" func.
   // DTRACE_MONITOR_PROBE(waited, monitor, obj(), THREAD);
   return dtrace_waited_probe(monitor, obj, THREAD);
@@ -715,11 +736,18 @@
     assert(!obj->mark().has_bias_pattern(), "biases should be revoked by now");
   }
   if (millis < 0) {
     THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
   }
-  inflate(THREAD, obj(), inflate_cause_wait)->wait(millis, false, THREAD);
+  ObjectMonitor* monitor = inflate(THREAD, obj(), inflate_cause_wait);
+  TSAN_ONLY(int tsan_rec;)
+  TSAN_RUNTIME_ONLY(
+    tsan_rec = SharedRuntime::tsan_oop_rec_unlock(THREAD, obj());
+    assert(tsan_rec > 0, "tsan: unlocking unlocked mutex");
+  );
+  monitor->wait(millis, false, THREAD);
+  TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_rec_lock(THREAD, obj(), tsan_rec));
 }
 
 void ObjectSynchronizer::notify(Handle obj, TRAPS) {
   if (UseBiasedLocking) {
     BiasedLocking::revoke(obj, THREAD);
@@ -2089,10 +2117,14 @@
 
  public:
   ReleaseJavaMonitorsClosure(Thread* thread) : THREAD(thread) {}
   void do_monitor(ObjectMonitor* mid) {
     if (mid->owner() == THREAD) {
+      // Note well -- this occurs ONLY on thread exit, and is a last ditch
+      // effort to release all locks. Hence, we don't need to record tsan's
+      // recursion count -- it will never be locked again.
+      TSAN_RUNTIME_ONLY(SharedRuntime::tsan_oop_rec_unlock(THREAD, (oop)mid->object()));
       (void)mid->complete_exit(CHECK);
     }
   }
 };
 
diff a/src/hotspot/share/tsan/tsan.cpp b/src/hotspot/share/tsan/tsan.cpp
--- /dev/null
+++ b/src/hotspot/share/tsan/tsan.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "classfile/tsanIgnoreList.hpp"
+#include "gc/shared/collectedHeap.hpp"
+#include "memory/universe.hpp"
+#include "oops/method.hpp"
+#include "runtime/java.hpp"
+#include "runtime/safepointVerifiers.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "tsan/tsanExternalDecls.hpp"
+#include "tsan/tsanOopMap.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+jint tsan_init() {
+  TsanOopMap::initialize_map();  // This is probably early enough.
+  TsanIgnoreList::init();
+  if (__tsan_java_init == NULL) {  // We always need tsan runtime functions.
+    vm_shutdown_during_initialization("libtsan cannot be located");
+    return JNI_ERR;
+  }
+  __tsan_java_init((julong)Universe::heap()->reserved_region().start(),
+                   (julong)Universe::heap()->reserved_region().byte_size());
+  return JNI_OK;
+}
+
+void tsan_exit() {
+  int status = __tsan_java_fini();
+  if (status != 0) {
+    vm_direct_exit(status);
+  }
+  TsanOopMap::destroy();
+}
+
+// The type of the callback TSAN passes to __tsan_symbolize_external_ex.
+// When __tsan_symbolize_external_ex has found a frame, it calls this callback,
+// passing along opaque context and frame's location (function name, file
+// where it is defined and line and column numbers). Note that we always pass
+// -1 as a column.
+typedef void (*AddFrameFunc)(void *ctx, const char *function, const char *file,
+                             int line, int column);
+
+static void TsanSymbolizeMethod(Method* m, u2 bci, AddFrameFunc add_frame,
+                                void* ctx) {
+  char methodname_buf[256];
+  char filename_buf[128];
+
+  m->name_and_sig_as_C_string(methodname_buf, sizeof(methodname_buf));
+  Symbol* filename = m->method_holder()->source_file_name();
+  if (filename != NULL) {
+    filename->as_C_string(filename_buf, sizeof(filename_buf));
+  } else {
+    filename_buf[0] = filename_buf[1] = '?';
+    filename_buf[2] = '\0';
+  }
+
+  add_frame(
+      ctx, methodname_buf, filename_buf, m->line_number_from_bci(bci), -1);
+}
+
+extern "C" {
+// TSAN calls this to symbolize Java frames.
+JNIEXPORT void TsanSymbolize(julong loc,
+                             AddFrameFunc add_frame,
+                             void *ctx) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(ThreadSanitizer, "Need -XX:+ThreadSanitizer");
+
+  assert((loc & SharedRuntime::tsan_fake_pc_bit) != 0,
+         "TSAN should only ask the JVM to symbolize locations the JVM gave TSAN"
+        );
+
+  jmethodID method_id = SharedRuntime::tsan_method_id_from_code_location(loc);
+  u2 bci = SharedRuntime::tsan_bci_from_code_location(loc);
+  Method *m;
+  if (method_id == 0) {
+    add_frame(
+        ctx, bci == 0 ? "(Generated Stub)" : "(Unknown Method)", NULL, -1, -1);
+  } else if ((m = Method::checked_resolve_jmethod_id(method_id)) != NULL) {
+    // Find a method by its jmethod_id. May fail if method has vanished since.
+    TsanSymbolizeMethod(m, bci, add_frame, ctx);
+  } else {
+    add_frame(ctx, "(Deleted method)", NULL, -1, -1);
+  }
+}
+}
+
+void TsanRawLockAcquired(const char *file, int line,
+                         const volatile void *lock) {
+  AnnotateRWLockAcquired(file, line, lock, 1);
+}
+
+void TsanRawLockReleased(const char *file, int line,
+                         const volatile void *lock) {
+  AnnotateRWLockReleased(file, line, lock, 1);
+}
+
+void TsanRawLockCreate(const char *file, int line, const volatile void *lock) {
+  AnnotateRWLockCreate(file, line, lock);
+}
+
+void TsanRawLockDestroy(const char *file, int line, const volatile void *lock) {
+  AnnotateRWLockDestroy(file, line, lock);
+}
diff a/src/hotspot/share/tsan/tsanOopMap.cpp b/src/hotspot/share/tsan/tsanOopMap.cpp
--- /dev/null
+++ b/src/hotspot/share/tsan/tsanOopMap.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/shared/gcTraceTime.inline.hpp"
+#include "gc/shared/gcId.hpp"
+#include "gc/shared/referenceProcessor.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "runtime/safepointVerifiers.hpp"
+#include "tsan/tsanExternalDecls.hpp"
+#include "tsan/tsanOopMap.hpp"
+#include "utilities/bitMap.inline.hpp"
+
+extern "C" int jio_printf(const char *fmt, ...);
+
+#if 0
+#define DEBUG_PRINT(...) jio_printf(__VA_ARGS__)
+#else
+#define DEBUG_PRINT(...)
+#endif
+namespace TsanOopMapImpl {
+
+  struct PendingMove {
+    char *source_begin() const { return source_address; }
+    char *source_end() const { return source_address + n_bytes; }
+    char *target_begin() const { return target_address; }
+    char *target_end() const { return target_address + n_bytes; }
+    char *source_address;
+    char *target_address;
+    size_t n_bytes;  // number of bytes being moved
+  };
+
+  // Our data
+  class TsanOopSizeMap *oop_map = NULL;
+
+  /**
+   * TsanOopSizeMap is a hash map of {oopDesc * -> size}.
+   */
+  class TsanOopSizeMap : public CHeapObj<mtInternal> {
+
+    class TsanOop : public CHeapObj<mtInternal> {
+      /* We track the lifecycle (alloc/move/free) of interesting oops;
+       * tsan needs to know. */
+      oopDesc *_oop;  // key
+
+      /* We cache the oop's size, since we cannot reliably determine it after
+       * the oop is freed. Size is measured in number of HeapWords. */
+      uintx _oop_size;  // value
+
+    public:
+      TsanOop():_oop(NULL), _oop_size(0) {}
+      void set_oop(oopDesc *o, uintx s) { _oop = o; _oop_size = s; }
+      bool has_oop() const { return _oop != NULL; }
+      oopDesc *get_oop() const { return _oop; }
+      uintx get_oop_size() const { return _oop_size; }
+    };
+
+    size_t _size;
+    size_t _n_elements;
+    float _load_factor;
+    TsanOop *_buckets;
+
+    static uintx _hash64(uintx key) {
+      key = ~key + (key << 21);
+      key ^= (key >> 24);
+      key += (key << 3) + (key << 8);
+      key ^= (key >> 14);
+      key += (key << 2) + (key << 4);
+      key ^= (key >> 28);
+      key += (key << 31);
+      return key;
+    }
+
+    static uintx _hash32(uintx key) {
+      key = ~key + (key << 15);
+      key ^= (key >> 12);
+      key += (key << 2);
+      key ^= (key >> 4);
+      key *= 2057;
+      key ^= (key >> 16);
+      return key;
+    }
+
+    TsanOop* find_bucket(oopDesc* o) {
+      uintx h = reinterpret_cast<uintx>((address)o);
+      TsanOop* bucket;
+      do {
+        h = hash(h);
+        bucket = &_buckets[h % _size];
+      } while (bucket->has_oop() && bucket->get_oop() != o);
+      return bucket;
+    }
+
+    static bool collect_oops(BoolObjectClosure* is_alive,
+                             OopClosure* f,
+                             GrowableArray<PendingMove>* moves,
+                             int* n_downward_moves,
+                             char** min_low,
+                             char** max_high);
+
+    static void handle_overlapping_moves(GrowableArray<PendingMove>& moves,
+                                         char* min_low,
+                                         char* max_high);
+
+  public:
+    TsanOopSizeMap(size_t initial_size)
+        : _size(initial_size), _n_elements(0), _load_factor(0.7) {
+      _buckets = new TsanOop[_size];
+    }
+
+    ~TsanOopSizeMap() {
+      delete [] _buckets;
+    }
+
+    static uintx hash(uintx key) {
+      return (sizeof(uintx) == 4) ? _hash32(key) : _hash64(key);
+    }
+
+    // Put an oop and oop size into the hash map.
+    // Ok to call multiple times on same oop.
+    // Return true if seen for first time; else return false.
+    // Synchronized in mutator threads with TsanOopMap_lock.
+    bool put(oopDesc* o, uintx s) {
+      TsanOop* bucket = find_bucket(o);
+
+      if (!bucket->has_oop()) {
+        if (++_n_elements > _load_factor * _size) {
+          grow();
+          bucket = find_bucket(o);
+        }
+        bucket->set_oop(o, s);
+        return true;
+      } else {
+        assert(s == bucket->get_oop_size(), "same oop should have same size");
+        return false;
+      }
+    }
+
+    void grow(void) {
+      TsanOop *old_buckets = _buckets;
+      size_t old_size = _size;
+      _size *= 2;
+
+      _buckets = new TsanOop[_size];
+
+      for (uintx i = 0; i < old_size; i++) {
+        if (old_buckets[i].has_oop()) {
+          put(old_buckets[i].get_oop(), old_buckets[i].get_oop_size());
+        }
+      }
+      delete [] old_buckets;
+    }
+
+    // Call this function at the end of the garbage collection to
+    // notify TSan about object location changes and to build oops map.
+    static void rebuild_oops_map(BoolObjectClosure *is_alive,
+                                 OopClosure *pointer_adjuster);
+
+#ifdef ASSERT
+    bool exists(oopDesc *o) const {
+      uintx h = reinterpret_cast<uintx>((address)o);
+      TsanOop *bucket = NULL;
+
+      do {
+        h = hash(h);
+        bucket = &_buckets[h % _size];
+      } while (bucket->has_oop() && bucket->get_oop() != o);
+
+      return bucket->has_oop() && bucket->get_oop() == o;
+    }
+#endif
+
+    size_t size() const { return _size; }
+    oopDesc *oop_at(size_t i) const { return _buckets[i].get_oop(); }
+    uintx oop_size_at(size_t i) const { return _buckets[i].get_oop_size(); }
+  };
+
+  // Two little callbacks used by sort.
+  int lessThan(PendingMove *l, PendingMove *r) {
+    char *left = l->target_begin();
+    char *right = r->target_begin();
+    return (left < right) ? -1 : (left == right ? 0 : 1);
+  }
+
+  int moreThan(PendingMove *l, PendingMove *r) {
+    return lessThan(r, l);
+  }
+
+  // Maintains the occupancy state of the given heap memory area.
+  // TsanOopSizeMap::rebuild_oop_map below uses an instance of this
+  // class to order object moves, please see additional comments there.
+  class OccupancyMap: public StackObj {
+    // Internally it is a BitMap. A bit is set if the corresponding HeapWord
+    // is currently occupied, cleared otherwise (HeapWord is Java object
+    // allocation unit).
+    char *mem_begin_;
+    char *mem_end_;
+    CHeapBitMap bitmap_;
+    BitMap::idx_t to_idx(char *mem) const {
+      return (mem - mem_begin_) / HeapWordSize;
+    }
+  public:
+    // NOTE: The constructor creates a bitmap on the resource area.
+    // The bitmap can be quite large (it is 16MB per every 1GB of heap,
+    // so it is worth releasing it as soon as possible by creating a
+    // ResourceMark.
+    OccupancyMap(char *mem_begin, char *mem_end)
+        : mem_begin_(mem_begin), mem_end_(mem_end),
+          bitmap_((mem_end - mem_begin) / HeapWordSize) {}
+    bool is_range_vacant(char *from, char *to) const {
+      assert(from < to, "bad range");
+      assert(from >= mem_begin_ && from < mem_end_,
+             "start address outside range");
+      assert(to > mem_begin_ && to <= mem_end_, "end address outside range");
+      BitMap::idx_t idx_to = to_idx(to);
+      return bitmap_.get_next_one_offset(to_idx(from), idx_to) == idx_to;
+    }
+    void range_occupy(char *from, char *to) {
+      assert(from < to, "range_occupy: bad range");
+      assert(from >= mem_begin_ && from < mem_end_,
+             "start address outside range");
+      assert(to > mem_begin_ && to <= mem_end_, "end address outside range");
+      bitmap_.set_range(to_idx(from), to_idx(to));
+    }
+    void range_vacate(char *from, char *to) {
+      assert(from < to, "bad range");
+      assert(from >= mem_begin_ && from < mem_end_,
+             "start address outside range");
+      assert(to > mem_begin_ && to <= mem_end_, "end address outside range");
+      bitmap_.clear_range(to_idx(from), to_idx(to));
+    }
+    int bit_count() const {
+      return bitmap_.size();
+    }
+  };
+
+  bool TsanOopSizeMap::collect_oops(BoolObjectClosure* is_alive,
+                                    OopClosure* pointer_adjuster,
+                                    GrowableArray<PendingMove>* moves,
+                                    int* n_downward_moves,
+                                    char** min_low,
+                                    char** max_high) {
+    size_t map_size = oop_map->size();
+
+    // Traverse oop map. For each object that survived GC calculate its new
+    // oop, add it to the new oop map, and append the move from the source oop
+    // to the target one to the moves list. While doing that, collect oop
+    // source and target ranges and count the moves that move an object
+    // downwards (this is heuristics to order the moves, see below).
+    TsanOopSizeMap* new_map = new TsanOopSizeMap(map_size / 2);
+    *n_downward_moves = 0;
+    bool disjoint_regions;
+    char *source_low = reinterpret_cast<char *>(UINTPTR_MAX);
+    char *source_high = NULL;
+    char *target_low = reinterpret_cast<char *>(UINTPTR_MAX);
+    char *target_high = NULL;
+    size_t deleted_objects = 0;
+    size_t unmoved_objects = 0;
+    size_t total_size_words = 0;
+    CollectedHeap *heap = Universe::heap();
+    for (size_t i = 0; i < map_size; i++) {
+      oopDesc *source_obj = oop_map->oop_at(i);
+
+      if (source_obj != NULL && heap->is_in(source_obj)) {
+        uintx obj_size = oop_map->oop_size_at(i);
+        size_t obj_size_bytes = obj_size * HeapWordSize;
+        if (is_alive->do_object_b(source_obj)) {
+          // The object survived GC, add its updated oop to the new oops map.
+          oop target_oop = cast_to_oop((intptr_t)source_obj);
+          pointer_adjuster->do_oop(&target_oop);
+          // The memory pointed by target_oop may not be a valid oop yet,
+          // for example the G1 full collector needs to adjust all pointers
+          // first, then compacts and moves the objects. In this case
+          // TsanOopSizeMap::rebuild_oops_map() is called during the adjust-
+          // pointer phase, before the collector moves the objects. Thus,
+          // we cannot use heap->is_in() or oopDesc::is_oop() to check
+          // target_oop.
+          assert(heap->is_in(target_oop), "Adjustment failed");
+          oopDesc *target_obj = target_oop;
+          new_map->put(target_obj, obj_size);
+          if (target_obj == source_obj) {
+            ++unmoved_objects;
+            continue;
+          }
+          if (target_obj < source_obj) {
+            ++(*n_downward_moves);
+          }
+          // Append to the moves list.
+          PendingMove move = {(char *)source_obj, (char *)target_obj,
+                              obj_size_bytes};
+          total_size_words += obj_size;
+          moves->append(move);
+
+          // Update source and target ranges.
+          source_low = MIN2(source_low, move.source_begin());
+          source_high = MAX2(source_high, move.source_end());
+          target_low = MIN2(target_low, move.target_begin());
+          target_high = MAX2(target_high, move.target_end());
+        } else {  // dead!
+          __tsan_java_free((char *)source_obj, obj_size_bytes);
+          ++deleted_objects;
+        }
+      }
+    }
+
+    // Update the oop map.
+    delete TsanOopMapImpl::oop_map;
+    TsanOopMapImpl::oop_map = new_map;
+
+    disjoint_regions = (source_low >= target_high || source_high <= target_low);
+    log_debug(gc)(
+          "Tsan: map of " SIZE_FORMAT " objects, " SIZE_FORMAT " deleted, "
+          SIZE_FORMAT " unmoved, " SIZE_FORMAT " to move "
+          "(" SIZE_FORMAT " words), %soverlap",
+          map_size, deleted_objects, unmoved_objects, (size_t)moves->length(),
+          total_size_words, disjoint_regions ? "no " : "");
+
+    *min_low = MIN2(source_low, target_low);
+    *max_high = MAX2(source_high, target_high);
+    return disjoint_regions;
+  }
+
+  void TsanOopSizeMap::handle_overlapping_moves(GrowableArray<PendingMove>& moves,
+                                                char* min_low,
+                                                char* max_high) {
+    // Populate occupied memory. The bitmap allocated by the OccupancyMap can
+    // be fairly large, scope this code and insert a ResourceMark
+    ResourceMark rm;
+    OccupancyMap occupied_memory(min_low, max_high);
+    DEBUG_PRINT("%s:%d: %d objects occupying %d words between %p and %p\n",
+                __FUNCTION__, __LINE__, moves.length(),
+                occupied_memory.bit_count(),
+                MIN2(source_low, target_low),
+                MAX2(source_high, target_high));
+    for (int i = 0; i < moves.length(); ++i) {
+      PendingMove &m = moves.at(i);
+      occupied_memory.range_occupy(m.source_begin(), m.source_end());
+    }
+
+    // Keep traversing moves list until everything is moved
+    int passes = 0;
+    for (int remaining_moves = moves.length(); remaining_moves > 0; ) {
+      ++passes;
+      int moves_this_cycle = 0;
+      for (int i = 0; i < moves.length(); ++i) {
+        if (moves.at(i).n_bytes == 0) {
+           // Already moved this one.
+           continue;
+        }
+
+        // Check if this move is currently possible.
+        // For this, everything in the target region that is not in the source
+        // region has to be vacant.
+        bool can_move;
+        PendingMove &m = moves.at(i);
+        if (m.target_begin() < m.source_begin()) {
+          // '+++++++' is region being moved, lower addresses are to the left:
+          // Moving downwards:
+          //         ++++++++         SOURCE
+          //    ++++++++              TARGET
+          // or
+          //              ++++++++    SOURCE
+          //    ++++++++              TARGET
+          can_move = occupied_memory.is_range_vacant(
+              m.target_begin(), MIN2(m.target_end(), m.source_begin()));
+        } else {
+          // Moving upwards:
+          //    ++++++++              SOURCE
+          //         ++++++++         TARGET
+          // or
+          //    ++++++++              SOURCE
+          //              ++++++++    TARGET
+          can_move = occupied_memory.is_range_vacant(
+              MAX2(m.source_end(), m.target_begin()), m.target_end());
+        }
+        if (can_move) {
+          // Notify TSan, update occupied region.
+          __tsan_java_move(m.source_begin(), m.target_begin(), m.n_bytes);
+          occupied_memory.range_vacate(m.source_begin(), m.source_end());
+          occupied_memory.range_occupy(m.target_begin(), m.target_end());
+          // Indicate that this move has been done and remember that we
+          // made some progress.
+          m.n_bytes = 0;
+          ++moves_this_cycle;
+        }
+      }
+      // We have to make some progress, otherwise bail out:
+      guarantee(moves_this_cycle, "Impossible to reconcile GC");
+
+      guarantee(remaining_moves >= moves_this_cycle,
+                "Excessive number of moves");
+      remaining_moves -= moves_this_cycle;
+      DEBUG_PRINT("%s:%d: %d moved, %d remaining\n", __FUNCTION__, __LINE__,
+                  moves_this_cycle, remaining_moves);
+    }
+    log_debug(gc)("Tsan: Move %d passes", passes);
+  }
+
+  void TsanOopSizeMap::rebuild_oops_map(BoolObjectClosure *is_alive,
+                                        OopClosure *pointer_adjuster) {
+    ResourceMark rm;
+    GCTraceTime(Debug, gc) tt_top("Tsan relocate");
+    GCTraceCPUTime tcpu;
+    GrowableArray<PendingMove> moves(MAX2((int)(oop_map->size() / 100),
+                                          100000));
+    bool disjoint_regions;
+    int n_downward_moves;
+    char *min_low, *max_high;
+
+    {
+      GCTraceTime(Debug, gc) tt_collect("Collect oops");
+      disjoint_regions = collect_oops(is_alive, pointer_adjuster, &moves,
+                                      &n_downward_moves, &min_low, &max_high);
+    }
+    if (moves.length() == 0) {
+      return;
+    }
+
+    // Notifying TSan is straightforward when source and target regions
+    // do not overlap:
+    if (disjoint_regions) {
+      GCTraceTime(Debug, gc) tt_disjoint("Move between regions");
+
+      for (int i = 0; i < moves.length(); ++i) {
+        const PendingMove &m = moves.at(i);
+        __tsan_java_move(m.source_begin(), m.target_begin(), m.n_bytes);
+      }
+      return;
+    }
+
+    // Source and target ranges overlap, the moves need to be ordered to prevent
+    // overwriting. Overall, this can take N^2 steps if only one object can be
+    // moved during the array traversal; however, when we are dealing with
+    // compacting garbage collector, observation shows that the overwhelming
+    // majority of the objects move in one direction. If we sort the moves (in
+    // the ascending order if dominant direction is downwards, in the descending
+    // order otherwise), chances are we will be able to order the moves in a few
+    // traversals of the moves array.
+    {
+      GCTraceTime(Debug, gc) tt_sort("Sort moves");
+
+      moves.sort((2 * n_downward_moves > moves.length()) ? lessThan : moreThan);
+      log_debug(gc)("Tsan: sort %d objects", moves.length());
+    }
+
+    {
+      GCTraceTime(Debug, gc) tt_sort("Move");
+      handle_overlapping_moves(moves, min_low, max_high);
+    }
+  }
+
+}  // namespace TsanOopMapImpl
+
+
+void TsanOopMap::initialize_map() {
+  TsanOopMapImpl::oop_map = new TsanOopMapImpl::TsanOopSizeMap(512);
+}
+
+void TsanOopMap::destroy() {
+  delete TsanOopMapImpl::oop_map;
+}
+
+void TsanOopMap::weak_oops_do(
+    BoolObjectClosure* is_alive,
+    OopClosure* pointer_adjuster) {
+  if (!ThreadSanitizer) return;
+  assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
+
+  // We're mutating oopMap, but we don't need to acquire TsanOopMap_lock:
+  // Mutation to map happens at (A) constructor (single threaded) and
+  // (B) add (in mutator threads) and (C) do_weak_oops (single-threaded).
+  // Calls between add are synchronized.
+  // Calls between add and do_weak_oops are synchronized via STW GC.
+  TsanOopMapImpl::TsanOopSizeMap::rebuild_oops_map(
+      is_alive, pointer_adjuster);
+}
+
+// Safe to deal with raw oop; for example this is called in a LEAF function
+// There is no safepoint in this code: 1) special mutex is used, and
+// 2) there is no VM state transition
+// We cannot use ordinary VM mutex, as that requires a state transition.
+void TsanOopMap::add_oop_with_size(oopDesc *addr, int size) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(TsanOopMapImpl::oop_map != NULL, "TsanOopMap not initialized");
+  guarantee(addr != NULL, "null oop");
+  bool alloc = false;
+  {
+    MutexLocker mu(TsanOopMap_lock, Mutex::_no_safepoint_check_flag);
+    // N.B. addr->size() may not be available yet!
+    alloc = TsanOopMapImpl::oop_map->put(addr, size);
+  }
+  if (alloc) {
+    __tsan_java_alloc(addr, size * HeapWordSize);
+  }
+}
+
+void TsanOopMap::add_oop(oopDesc *addr) {
+  // N.B. oop's size field must be init'ed; else addr->size() crashes.
+  TsanOopMap::add_oop_with_size(addr, addr->size());
+}
+
+#ifdef ASSERT
+bool TsanOopMap::exists(oopDesc *addr) {
+  DEBUG_ONLY(NoSafepointVerifier nsv;)
+  assert(TsanOopMapImpl::oop_map != NULL, "TsanOopMap not initialized");
+  guarantee(addr != NULL, "null oop");
+  bool in_map = false;
+  {
+    MutexLocker mu(TsanOopMap_lock, Mutex::_no_safepoint_check_flag);
+    in_map = TsanOopMapImpl::oop_map->exists(addr);
+  }
+  return in_map;
+}
+#endif
diff a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp
--- a/src/hotspot/share/utilities/macros.hpp
+++ b/src/hotspot/share/utilities/macros.hpp
@@ -265,10 +265,29 @@
 #else
 #define JFR_ONLY(code)
 #define NOT_JFR_RETURN_(code) { return code; }
 #endif
 
+#ifndef INCLUDE_TSAN
+#define INCLUDE_TSAN 1
+#endif
+
+#if INCLUDE_TSAN
+#define TSAN_ONLY(code) code
+#define TSAN_RUNTIME_ONLY(code) \
+    do { \
+      if (ThreadSanitizer) { \
+        code; \
+      } \
+    } while (0)
+#define NOT_TSAN(code)
+#else
+#define TSAN_ONLY(code)
+#define TSAN_RUNTIME_ONLY(code)
+#define NOT_TSAN(code) code
+#endif
+
 #ifndef INCLUDE_JVMCI
 #define INCLUDE_JVMCI 1
 #endif
 
 #ifndef INCLUDE_AOT
diff a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -107,10 +107,11 @@
     exports java.time.format;
     exports java.time.temporal;
     exports java.time.zone;
     exports java.util;
     exports java.util.concurrent;
+    exports java.util.concurrent.annotation;
     exports java.util.concurrent.atomic;
     exports java.util.concurrent.locks;
     exports java.util.function;
     exports java.util.jar;
     exports java.util.regex;
diff a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c
--- a/src/java.base/share/native/libjli/java.c
+++ b/src/java.base/share/native/libjli/java.c
@@ -217,10 +217,18 @@
   */
 #ifndef STACK_SIZE_MINIMUM
 #define STACK_SIZE_MINIMUM (64 * KB)
 #endif
 
+#ifdef INCLUDE_TSAN
+/*
+ * Function pointer to JVM's TSAN symbolize function.
+ */
+__attribute__((visibility("default")))
+TsanSymbolize_t tsan_symbolize_func = NULL;
+#endif
+
 /*
  * Entry point.
  */
 JNIEXPORT int JNICALL
 JLI_Launch(int argc, char ** argv,              /* main argc, argv */
@@ -296,10 +304,13 @@
     }
 
     if (!LoadJavaVM(jvmpath, &ifn)) {
         return(6);
     }
+#ifdef INCLUDE_TSAN
+    tsan_symbolize_func = ifn.TsanSymbolize;
+#endif
 
     if (JLI_IsTraceLauncher()) {
         end   = CounterGet();
     }
 
diff a/src/java.base/share/native/libjli/java.h b/src/java.base/share/native/libjli/java.h
--- a/src/java.base/share/native/libjli/java.h
+++ b/src/java.base/share/native/libjli/java.h
@@ -71,10 +71,17 @@
 
 #define SPLASH_FILE_ENV_ENTRY "_JAVA_SPLASH_FILE"
 #define SPLASH_JAR_ENV_ENTRY "_JAVA_SPLASH_JAR"
 #define JDK_JAVA_OPTIONS "JDK_JAVA_OPTIONS"
 
+#ifdef INCLUDE_TSAN
+typedef void (*TsanSymbolizeAddFrameFunc)(
+    void *ctx, const char *function, const char *file, int line, int column);
+typedef void (JNICALL *TsanSymbolize_t)(uint64_t, TsanSymbolizeAddFrameFunc, void *);
+extern TsanSymbolize_t tsan_symbolize_func;
+#endif
+
 /*
  * Pointers to the needed JNI invocation API, initialized by LoadJavaVM.
  */
 typedef jint (JNICALL *CreateJavaVM_t)(JavaVM **pvm, void **env, void *args);
 typedef jint (JNICALL *GetDefaultJavaVMInitArgs_t)(void *args);
@@ -82,10 +89,13 @@
 
 typedef struct {
     CreateJavaVM_t CreateJavaVM;
     GetDefaultJavaVMInitArgs_t GetDefaultJavaVMInitArgs;
     GetCreatedJavaVMs_t GetCreatedJavaVMs;
+#ifdef INCLUDE_TSAN
+    TsanSymbolize_t TsanSymbolize;
+#endif
 } InvocationFunctions;
 
 JNIEXPORT int JNICALL
 JLI_Launch(int argc, char ** argv,              /* main argc, argc */
         int jargc, const char** jargv,          /* java args */
diff a/src/java.base/unix/native/libjli/java_md_solinux.c b/src/java.base/unix/native/libjli/java_md_solinux.c
--- a/src/java.base/unix/native/libjli/java_md_solinux.c
+++ b/src/java.base/unix/native/libjli/java_md_solinux.c
@@ -625,10 +625,19 @@
     if (ifn->GetCreatedJavaVMs == NULL) {
         JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
         return JNI_FALSE;
     }
 
+#ifdef INCLUDE_TSAN
+    ifn->TsanSymbolize = (TsanSymbolize_t)
+        dlsym(libjvm, "TsanSymbolize");
+    if (ifn->TsanSymbolize == NULL) {
+        JLI_ReportErrorMessage(DLL_ERROR2, jvmpath, dlerror());
+        return JNI_FALSE;
+    }
+#endif
+
     return JNI_TRUE;
 }
 
 /*
  * Compute the name of the executable
diff a/test/hotspot/jtreg/tsan/NonRacyGarbageCollectionLoopTest.java b/test/hotspot/jtreg/tsan/NonRacyGarbageCollectionLoopTest.java
--- /dev/null
+++ b/test/hotspot/jtreg/tsan/NonRacyGarbageCollectionLoopTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019 Google and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test NonRacyGarbageCollectionLoopTest
+ * @summary Test that objects are tracked correctly when garbage collection happens.
+ * @library /test/lib
+ * @requires vm.gc == null
+ * @build AbstractLoop TsanRunner
+ * @run main/othervm -XX:+UseParallelGC NonRacyGarbageCollectionLoopTest
+ * @run main/othervm -XX:+UseG1GC NonRacyGarbageCollectionLoopTest
+ * @run main/othervm -XX:+UseSerialGC NonRacyGarbageCollectionLoopTest
+ */
+
+import java.io.IOException;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class NonRacyGarbageCollectionLoopTest {
+  public static void main(String[] args) throws IOException {
+    TsanRunner.runTsanTestExpectSuccess(NonRacyGarbageCollectionLoopRunner.class, "-Xms40m", "-Xmx40m");
+  }
+}
+
+class NonRacyGarbageCollectionLoopRunner extends AbstractLoop {
+  private static final int MEG = 1024 * 1024;
+  // NOTE: If HEAP_SIZE changes, make sure also change -Xms and -Xmx values above.
+  private static final int HEAP_SIZE = 40 * MEG;
+  // Allocate byte arrays that sum up to about 4 times of heap size,
+  // assuming 2 threads and each allocates AbstractLoop.LOOPS times.
+  // Thus garbage collection must have happened and the same address
+  // will be reused to allocate a new object.
+  private static final int BYTE_ARRAY_LENGTH = HEAP_SIZE * 4 / 2 / AbstractLoop.LOOPS;
+
+  private volatile byte[] array;
+
+  @Override
+  protected void run(int i) {
+    byte[] arr = new byte[BYTE_ARRAY_LENGTH];
+    for (int j = 0; j < BYTE_ARRAY_LENGTH; j++) {
+      arr[j] = 42;
+    }
+    array = arr;
+  }
+
+  public static void main(String[] args) throws InterruptedException {
+    NonRacyGarbageCollectionLoopRunner loop = new NonRacyGarbageCollectionLoopRunner();
+    loop.runInTwoThreads();
+    System.out.println("array[0] = " + loop.array[0]);
+  }
+}
