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
@@ -295,10 +295,24 @@
 bool volatile ObjectSynchronizer::_is_final_audit = false;
 jlong ObjectSynchronizer::_last_async_deflation_time_ns = 0;
 static uintx _no_progress_cnt = 0;
 static bool _no_progress_skip_increment = false;
 
+#define CHECK_THROW_NOSYNC_IMSE(obj)  \
+  if (EnableValhalla && (obj)->mark().is_inline_type()) {  \
+    JavaThread* THREAD = current;           \
+    ResourceMark rm(THREAD);                \
+    THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \
+  }
+
+#define CHECK_THROW_NOSYNC_IMSE_0(obj)  \
+  if (EnableValhalla && (obj)->mark().is_inline_type()) {  \
+    JavaThread* THREAD = current;             \
+    ResourceMark rm(THREAD);                  \
+    THROW_MSG_0(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \
+  }
+
 // =====================> Quick functions
 
 // The quick_* forms are special fast-path variants used to improve
 // performance.  In the simplest case, a "quick_*" implementation could
 // simply return false, in which case the caller will perform the necessary
@@ -321,10 +335,11 @@
 
 bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool all) {
   assert(current->thread_state() == _thread_in_Java, "invariant");
   NoSafepointVerifier nsv;
   if (obj == nullptr) return false;  // slow-path for invalid obj
+  assert(!EnableValhalla || !obj->klass()->is_inline_klass(), "monitor op on inline type");
   const markWord mark = obj->mark();
 
   if (LockingMode == LM_LIGHTWEIGHT) {
     if (mark.is_fast_locked() && current->lock_stack().contains(cast_to_oop(obj))) {
       // Degenerate notify
@@ -377,10 +392,11 @@
 bool ObjectSynchronizer::quick_enter(oop obj, JavaThread* current,
                                      BasicLock * lock) {
   assert(current->thread_state() == _thread_in_Java, "invariant");
   NoSafepointVerifier nsv;
   if (obj == nullptr) return false;       // Need to throw NPE
+  assert(!EnableValhalla || !obj->klass()->is_inline_klass(), "monitor op on inline type");
 
   if (obj->klass()->is_value_based()) {
     return false;
   }
 
@@ -498,10 +514,11 @@
 // The interpreter and compiler assembly code tries to lock using the fast path
 // of this algorithm. Make sure to update that code if the following function is
 // changed. The implementation is extremely sensitive to race condition. Be careful.
 
 void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
+  CHECK_THROW_NOSYNC_IMSE(obj);
   if (obj->klass()->is_value_based()) {
     handle_sync_on_value_based_class(obj, current);
   }
 
   current->inc_held_monitor_count();
@@ -567,10 +584,13 @@
 void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
   current->dec_held_monitor_count();
 
   if (!useHeavyMonitors()) {
     markWord mark = object->mark();
+    if (EnableValhalla && mark.is_inline_type()) {
+      return;
+    }
     if (LockingMode == LM_LIGHTWEIGHT) {
       // Fast-locking does not use the 'lock' argument.
       if (mark.is_fast_locked()) {
         markWord unlocked_mark = mark.set_unlocked();
         markWord old_mark = object->cas_set_mark(unlocked_mark, mark);
@@ -646,10 +666,11 @@
 // NOTE: must use heavy weight monitor to handle jni monitor enter
 void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
   if (obj->klass()->is_value_based()) {
     handle_sync_on_value_based_class(obj, current);
   }
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   // the current locking is from JNI instead of Java code
   current->set_current_pending_monitor_is_from_java(false);
   // An async deflation can race after the inflate() call and before
   // enter() can make the ObjectMonitor busy. enter() returns false if
@@ -665,10 +686,11 @@
 }
 
 // NOTE: must use heavy weight monitor to handle jni monitor exit
 void ObjectSynchronizer::jni_exit(oop obj, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   // The ObjectMonitor* can't be async deflated until ownership is
   // dropped inside exit() and the ObjectMonitor* must be !is_busy().
   ObjectMonitor* monitor = inflate(current, obj, inflate_cause_jni_exit);
   // If this thread has locked the object, exit the monitor. We
@@ -703,10 +725,11 @@
 // -----------------------------------------------------------------------------
 //  Wait/Notify/NotifyAll
 // NOTE: must use heavy weight monitor to handle wait()
 int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE_0(obj);
   if (millis < 0) {
     THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
   }
   // The ObjectMonitor* can't be async deflated because the _waiters
   // field is incremented before ownership is dropped and decremented
@@ -724,10 +747,11 @@
   return ret_code;
 }
 
 void ObjectSynchronizer::notify(Handle obj, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   markWord mark = obj->mark();
   if (LockingMode == LM_LIGHTWEIGHT) {
     if ((mark.is_fast_locked() && current->lock_stack().contains(obj()))) {
       // Not inflated so there can't be any waiters to notify.
@@ -746,10 +770,11 @@
 }
 
 // NOTE: see comment of notify()
 void ObjectSynchronizer::notifyall(Handle obj, TRAPS) {
   JavaThread* current = THREAD;
+  CHECK_THROW_NOSYNC_IMSE(obj);
 
   markWord mark = obj->mark();
   if (LockingMode == LM_LIGHTWEIGHT) {
     if ((mark.is_fast_locked() && current->lock_stack().contains(obj()))) {
       // Not inflated so there can't be any waiters to notify.
@@ -907,10 +932,14 @@
   assert(LockingMode == LM_LIGHTWEIGHT, "only call this with new lightweight locking enabled");
   return thread->is_Java_thread() ? JavaThread::cast(thread)->lock_stack().contains(obj) : false;
 }
 
 intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
+  if (EnableValhalla && obj->klass()->is_inline_klass()) {
+    // VM should be calling bootstrap method
+    ShouldNotReachHere();
+  }
 
   while (true) {
     ObjectMonitor* monitor = nullptr;
     markWord temp, test;
     intptr_t hash;
@@ -1027,10 +1056,13 @@
   }
 }
 
 bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* current,
                                                    Handle h_obj) {
+  if (EnableValhalla && h_obj->mark().is_inline_type()) {
+    return false;
+  }
   assert(current == JavaThread::current(), "Can only be called on current thread");
   oop obj = h_obj();
 
   markWord mark = read_stable_mark(obj);
 
@@ -1299,10 +1331,14 @@
   (void)inflate(Thread::current(), obj, inflate_cause_vm_internal);
 }
 
 ObjectMonitor* ObjectSynchronizer::inflate(Thread* current, oop object,
                                            const InflateCause cause) {
+  if (EnableValhalla) {
+    guarantee(!object->klass()->is_inline_klass(), "Attempt to inflate inline type");
+  }
+
   EventJavaMonitorInflate event;
 
   for (;;) {
     const markWord mark = object->mark_acquire();
 
