Revert "Initial support for adding virtuals with structural redefinition"

This reverts commit 283bb322de84ac570b987c65a1015e2dbcbfad7c.

Reason for revert: Test flakes

Bug: 134162467
Bug: 144168550
Bug: 144590579
Change-Id: I2259c030f03a72f6b7bcda10288bd23cb3258164
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index 37ae951..e7306ba 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -26,7 +26,6 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
-        "alloc_manager.cc",
         "deopt_manager.cc",
         "events.cc",
         "fixed_up_dex_file.cc",
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 4ce376f..665fa9f 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -40,7 +40,6 @@
 
 #include "jvmti.h"
 
-#include "alloc_manager.h"
 #include "art_jvmti.h"
 #include "base/logging.h"  // For gLogVerbosity.
 #include "base/mutex.h"
@@ -80,7 +79,6 @@
 // These should never be null.
 EventHandler* gEventHandler;
 DeoptManager* gDeoptManager;
-AllocationManager* gAllocManager;
 
 #define ENSURE_NON_NULL(n)      \
   do {                          \
@@ -1499,7 +1497,6 @@
 extern "C" bool ArtPlugin_Initialize() {
   art::Runtime* runtime = art::Runtime::Current();
 
-  gAllocManager = new AllocationManager;
   gDeoptManager = new DeoptManager;
   gEventHandler = new EventHandler;
 
diff --git a/openjdkjvmti/alloc_manager.cc b/openjdkjvmti/alloc_manager.cc
deleted file mode 100644
index 597ab05..0000000
--- a/openjdkjvmti/alloc_manager.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-
-/* Copyright (C) 2019 The Android Open Source Project
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This file implements interfaces from the file jvmti.h. This implementation
- * is licensed under the same terms as the file jvmti.h.  The
- * copyright and license information for the file jvmti.h follows.
- *
- * Copyright (c) 2003, 2011, Oracle 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 "alloc_manager.h"
-
-#include <atomic>
-#include <sstream>
-
-#include "base/logging.h"
-#include "gc/allocation_listener.h"
-#include "gc/heap.h"
-#include "handle.h"
-#include "mirror/class-inl.h"
-#include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "scoped_thread_state_change.h"
-#include "thread-current-inl.h"
-#include "thread_list.h"
-#include "thread_pool.h"
-
-namespace openjdkjvmti {
-
-template<typename T>
-void AllocationManager::PauseForAllocation(art::Thread* self, T msg) {
-  // The suspension can pause us for arbitrary times. We need to do it to sleep unfortunately. So we
-  // do test, suspend, test again, sleep, repeat.
-  std::string cause;
-  const bool is_logging = VLOG_IS_ON(plugin);
-  while (true) {
-    // We always return when there is no pause and we are runnable.
-    art::Thread* pausing_thread = allocations_paused_thread_.load(std::memory_order_seq_cst);
-    if (LIKELY(pausing_thread == nullptr || pausing_thread == self)) {
-      return;
-    }
-    if (UNLIKELY(is_logging && cause.empty())) {
-      cause = msg();
-    }
-    art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended);
-    art::MutexLock mu(self, alloc_listener_mutex_);
-    pausing_thread = allocations_paused_thread_.load(std::memory_order_seq_cst);
-    CHECK_NE(pausing_thread, self) << "We should always be setting pausing_thread = self!"
-                                   << " How did this happen? " << *self;
-    if (pausing_thread != nullptr) {
-      VLOG(plugin) << "Suspending " << *self << " due to " << cause << ". Allocation pause "
-                   << "initiated by " << *pausing_thread;
-      alloc_pause_cv_.Wait(self);
-    }
-  }
-}
-
-extern AllocationManager* gAllocManager;
-AllocationManager* AllocationManager::Get() {
-  return gAllocManager;
-}
-
-void JvmtiAllocationListener::ObjectAllocated(art::Thread* self,
-                                              art::ObjPtr<art::mirror::Object>* obj,
-                                              size_t cnt) {
-  auto cb = manager_->callback_;
-  if (cb != nullptr && manager_->callback_enabled_.load(std::memory_order_seq_cst)) {
-    cb->ObjectAllocated(self, obj, cnt);
-  }
-}
-
-bool JvmtiAllocationListener::HasPreAlloc() const {
-  return manager_->allocations_paused_thread_.load(std::memory_order_seq_cst) != nullptr;
-}
-
-void JvmtiAllocationListener::PreObjectAllocated(art::Thread* self,
-                                                 art::MutableHandle<art::mirror::Class> type,
-                                                 size_t* byte_count) {
-  manager_->PauseForAllocation(self, [&]() REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    std::ostringstream oss;
-    oss << "allocating " << *byte_count << " bytes of type " << type->PrettyClass();
-    return oss.str();
-  });
-  if (!type->IsVariableSize()) {
-    *byte_count = type->GetObjectSize();
-  }
-}
-
-AllocationManager::AllocationManager()
-    : alloc_listener_(nullptr),
-      alloc_listener_mutex_("JVMTI Alloc listener",
-                            art::LockLevel::kPostUserCodeSuspensionTopLevelLock),
-      alloc_pause_cv_("JVMTI Allocation Pause Condvar", alloc_listener_mutex_) {
-  alloc_listener_.reset(new JvmtiAllocationListener(this));
-}
-
-void AllocationManager::DisableAllocationCallback(art::Thread* self) {
-  callback_enabled_.store(false);
-  DecrListenerInstall(self);
-}
-
-void AllocationManager::EnableAllocationCallback(art::Thread* self) {
-  IncrListenerInstall(self);
-  callback_enabled_.store(true);
-}
-
-void AllocationManager::SetAllocListener(AllocationCallback* callback) {
-  CHECK(callback_ == nullptr) << "Already setup!";
-  callback_ = callback;
-  alloc_listener_.reset(new JvmtiAllocationListener(this));
-}
-
-void AllocationManager::RemoveAllocListener() {
-  callback_enabled_.store(false, std::memory_order_seq_cst);
-  callback_ = nullptr;
-}
-
-void AllocationManager::DecrListenerInstall(art::Thread* self) {
-  art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended);
-  art::MutexLock mu(self, alloc_listener_mutex_);
-  // We don't need any particular memory-order here since we're under the lock, they aren't
-  // changing.
-  if (--listener_refcount_ == 0) {
-    art::Runtime::Current()->GetHeap()->RemoveAllocationListener();
-  }
-}
-
-void AllocationManager::IncrListenerInstall(art::Thread* self) {
-  art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended);
-  art::MutexLock mu(self, alloc_listener_mutex_);
-  // We don't need any particular memory-order here since we're under the lock, they aren't
-  // changing.
-  if (listener_refcount_++ == 0) {
-    art::Runtime::Current()->GetHeap()->SetAllocationListener(alloc_listener_.get());
-  }
-}
-
-void AllocationManager::PauseAllocations(art::Thread* self) {
-  art::Thread* null_thr = nullptr;
-  IncrListenerInstall(self);
-  do {
-    PauseForAllocation(self, []() { return "request to pause allocations on other threads"; });
-  } while (allocations_paused_thread_.compare_exchange_strong(
-      null_thr, self, std::memory_order_seq_cst));
-  // Make sure everything else can see this and isn't in the middle of final allocation.
-  // Force every thread to either be suspended or pass through a barrier.
-  art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended);
-  art::Barrier barrier(0);
-  art::FunctionClosure fc([&](art::Thread* thr ATTRIBUTE_UNUSED) {
-    barrier.Pass(art::Thread::Current());
-  });
-  size_t requested = art::Runtime::Current()->GetThreadList()->RunCheckpoint(&fc);
-  barrier.Increment(self, requested);
-}
-
-void AllocationManager::ResumeAllocations(art::Thread* self) {
-  CHECK_EQ(allocations_paused_thread_.load(), self) << "not paused! ";
-  DecrListenerInstall(self);
-  art::ScopedThreadSuspension sts(self, art::ThreadState::kSuspended);
-  art::MutexLock mu(self, alloc_listener_mutex_);
-  allocations_paused_thread_.store(nullptr, std::memory_order_seq_cst);
-  alloc_pause_cv_.Broadcast(self);
-}
-
-}  // namespace openjdkjvmti
diff --git a/openjdkjvmti/alloc_manager.h b/openjdkjvmti/alloc_manager.h
deleted file mode 100644
index c89d9a6..0000000
--- a/openjdkjvmti/alloc_manager.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Copyright (C) 2019 The Android Open Source Project
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This file implements interfaces from the file jvmti.h. This implementation
- * is licensed under the same terms as the file jvmti.h.  The
- * copyright and license information for the file jvmti.h follows.
- *
- * Copyright (c) 2003, 2011, Oracle 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-
-#ifndef ART_OPENJDKJVMTI_ALLOC_MANAGER_H_
-#define ART_OPENJDKJVMTI_ALLOC_MANAGER_H_
-
-#include <jvmti.h>
-
-#include <atomic>
-
-#include "base/locks.h"
-#include "base/mutex.h"
-#include "gc/allocation_listener.h"
-
-namespace art {
-template <typename T> class MutableHandle;
-template <typename T> class ObjPtr;
-class Thread;
-namespace mirror {
-class Class;
-class Object;
-}  // namespace mirror
-}  // namespace art
-
-namespace openjdkjvmti {
-
-class AllocationManager;
-
-class JvmtiAllocationListener : public art::gc::AllocationListener {
- public:
-  explicit JvmtiAllocationListener(AllocationManager* manager) : manager_(manager) {}
-  void ObjectAllocated(art::Thread* self,
-                       art::ObjPtr<art::mirror::Object>* obj,
-                       size_t cnt) override REQUIRES_SHARED(art::Locks::mutator_lock_);
-  bool HasPreAlloc() const override REQUIRES_SHARED(art::Locks::mutator_lock_);
-  void PreObjectAllocated(art::Thread* self,
-                          art::MutableHandle<art::mirror::Class> type,
-                          size_t* byte_count) override REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- private:
-  AllocationManager* manager_;
-};
-
-class AllocationManager {
- public:
-  class AllocationCallback {
-   public:
-    virtual ~AllocationCallback() {}
-    virtual void ObjectAllocated(art::Thread* self,
-                                 art::ObjPtr<art::mirror::Object>* obj,
-                                 size_t byte_count) REQUIRES_SHARED(art::Locks::mutator_lock_) = 0;
-  };
-
-  AllocationManager();
-
-  void SetAllocListener(AllocationCallback* callback);
-  void RemoveAllocListener();
-
-  static AllocationManager* Get();
-
-  void PauseAllocations(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_);
-  void ResumeAllocations(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_);
-
-  void EnableAllocationCallback(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_);
-  void DisableAllocationCallback(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_);
-
- private:
-  template<typename T>
-  void PauseForAllocation(art::Thread* self, T msg) REQUIRES_SHARED(art::Locks::mutator_lock_);
-  void IncrListenerInstall(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_);
-  void DecrListenerInstall(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_);
-
-  AllocationCallback* callback_ = nullptr;
-  uint32_t listener_refcount_ GUARDED_BY(alloc_listener_mutex_) = 0;
-  std::atomic<art::Thread*> allocations_paused_thread_ = nullptr;
-  std::atomic<bool> callback_enabled_ = false;
-  std::unique_ptr<JvmtiAllocationListener> alloc_listener_ = nullptr;
-  art::Mutex alloc_listener_mutex_ ACQUIRED_AFTER(art::Locks::user_code_suspension_lock_);
-  art::ConditionVariable alloc_pause_cv_;
-
-  friend class JvmtiAllocationListener;
-};
-
-}  // namespace openjdkjvmti
-
-#endif  // ART_OPENJDKJVMTI_ALLOC_MANAGER_H_
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 64a02e8..56406fc 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -31,7 +31,6 @@
 
 #include <android-base/thread_annotations.h>
 
-#include "alloc_manager.h"
 #include "base/locks.h"
 #include "base/mutex.h"
 #include "events-inl.h"
@@ -313,9 +312,9 @@
   DISALLOW_COPY_AND_ASSIGN(JvmtiDdmChunkListener);
 };
 
-class JvmtiEventAllocationListener : public AllocationManager::AllocationCallback {
+class JvmtiAllocationListener : public art::gc::AllocationListener {
  public:
-  explicit JvmtiEventAllocationListener(EventHandler* handler) : handler_(handler) {}
+  explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {}
 
   void ObjectAllocated(art::Thread* self, art::ObjPtr<art::mirror::Object>* obj, size_t byte_count)
       override REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -350,14 +349,15 @@
   EventHandler* handler_;
 };
 
-static void SetupObjectAllocationTracking(bool enable) {
+static void SetupObjectAllocationTracking(art::gc::AllocationListener* listener, bool enable) {
   // We must not hold the mutator lock here, but if we're in FastJNI, for example, we might. For
   // now, do a workaround: (possibly) acquire and release.
   art::ScopedObjectAccess soa(art::Thread::Current());
+  art::ScopedThreadSuspension sts(soa.Self(), art::ThreadState::kSuspended);
   if (enable) {
-    AllocationManager::Get()->EnableAllocationCallback(soa.Self());
+    art::Runtime::Current()->GetHeap()->SetAllocationListener(listener);
   } else {
-    AllocationManager::Get()->DisableAllocationCallback(soa.Self());
+    art::Runtime::Current()->GetHeap()->RemoveAllocationListener();
   }
 }
 
@@ -1327,7 +1327,7 @@
       SetupDdmTracking(ddm_listener_.get(), enable);
       return;
     case ArtJvmtiEvent::kVmObjectAlloc:
-      SetupObjectAllocationTracking(enable);
+      SetupObjectAllocationTracking(alloc_listener_.get(), enable);
       return;
     case ArtJvmtiEvent::kGarbageCollectionStart:
     case ArtJvmtiEvent::kGarbageCollectionFinish:
@@ -1665,15 +1665,13 @@
   art::ScopedSuspendAll ssa("jvmti method tracing uninstallation");
   // Just remove every possible event.
   art::Runtime::Current()->GetInstrumentation()->RemoveListener(method_trace_listener_.get(), ~0);
-  AllocationManager::Get()->RemoveAllocListener();
 }
 
 EventHandler::EventHandler()
   : envs_lock_("JVMTI Environment List Lock", art::LockLevel::kPostMutatorTopLockLevel),
     frame_pop_enabled(false),
     internal_event_refcount_({0}) {
-  alloc_listener_.reset(new JvmtiEventAllocationListener(this));
-  AllocationManager::Get()->SetAllocListener(alloc_listener_.get());
+  alloc_listener_.reset(new JvmtiAllocationListener(this));
   ddm_listener_.reset(new JvmtiDdmChunkListener(this));
   gc_pause_listener_.reset(new JvmtiGcPauseListener(this));
   method_trace_listener_.reset(new JvmtiMethodTraceListener(this));
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index d4eb171..c9d587a 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -34,7 +34,7 @@
 namespace openjdkjvmti {
 
 struct ArtJvmTiEnv;
-class JvmtiEventAllocationListener;
+class JvmtiAllocationListener;
 class JvmtiDdmChunkListener;
 class JvmtiGcPauseListener;
 class JvmtiMethodTraceListener;
@@ -425,7 +425,7 @@
   // A union of all enabled events, anywhere.
   EventMask global_mask;
 
-  std::unique_ptr<JvmtiEventAllocationListener> alloc_listener_;
+  std::unique_ptr<JvmtiAllocationListener> alloc_listener_;
   std::unique_ptr<JvmtiDdmChunkListener> ddm_listener_;
   std::unique_ptr<JvmtiGcPauseListener> gc_pause_listener_;
   std::unique_ptr<JvmtiMethodTraceListener> method_trace_listener_;
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index b25b4d1..1d18390 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -17,7 +17,6 @@
 #include "ti_heap.h"
 
 #include <ios>
-#include <unordered_map>
 
 #include "android-base/logging.h"
 #include "android-base/thread_annotations.h"
@@ -1614,9 +1613,8 @@
 namespace {
 
 using ObjectPtr = art::ObjPtr<art::mirror::Object>;
-using ObjectMap = std::unordered_map<ObjectPtr, ObjectPtr, art::HashObjPtr>;
 
-static void ReplaceObjectReferences(const ObjectMap& map)
+static void ReplaceObjectReferences(ObjectPtr old_obj_ptr, ObjectPtr new_obj_ptr)
     REQUIRES(art::Locks::mutator_lock_,
              art::Roles::uninterruptible_) {
   art::Runtime::Current()->GetHeap()->VisitObjectsPaused(
@@ -1625,7 +1623,8 @@
         class ResizeReferenceVisitor {
          public:
           using CompressedObj = art::mirror::CompressedReference<art::mirror::Object>;
-          explicit ResizeReferenceVisitor(const ObjectMap& map) : map_(map) {}
+          ResizeReferenceVisitor(ObjectPtr old_arr, ObjectPtr new_arr)
+              : old_obj_(old_arr), new_obj_(new_arr) {}
 
           // Ignore class roots.
           void VisitRootIfNonNull(CompressedObj* root) const
@@ -1635,29 +1634,20 @@
             }
           }
           void VisitRoot(CompressedObj* root) const REQUIRES_SHARED(art::Locks::mutator_lock_) {
-            auto it = map_.find(root->AsMirrorPtr());
-            if (it != map_.end()) {
-              root->Assign(it->second);
-              art::WriteBarrier::ForEveryFieldWrite(it->second);
+            if (root->AsMirrorPtr() == old_obj_) {
+              root->Assign(new_obj_);
+              art::WriteBarrier::ForEveryFieldWrite(new_obj_);
             }
           }
 
           void operator()(art::ObjPtr<art::mirror::Object> obj,
                           art::MemberOffset off,
-                          bool is_static) const
+                          bool is_static ATTRIBUTE_UNUSED) const
               REQUIRES_SHARED(art::Locks::mutator_lock_) {
-            auto it = map_.find(obj->GetFieldObject<art::mirror::Object>(off));
-            if (it != map_.end()) {
-              UNUSED(is_static);
-              if (UNLIKELY(!is_static && off == art::mirror::Object::ClassOffset())) {
-                // We don't want to update the declaring class of any objects. They will be replaced
-                // in the heap and we need the declaring class to know its size.
-                return;
-              }
+            if (obj->GetFieldObject<art::mirror::Object>(off) == old_obj_) {
               VLOG(plugin) << "Updating field at offset " << off.Uint32Value() << " of type "
                            << obj->GetClass()->PrettyClass();
-              obj->SetFieldObject</*transaction*/ false>(off, it->second);
-              art::WriteBarrier::ForEveryFieldWrite(obj);
+              obj->SetFieldObject</*transaction*/ false>(off, new_obj_);
             }
           }
 
@@ -1669,10 +1659,11 @@
           }
 
          private:
-          const ObjectMap& map_;
+          ObjectPtr old_obj_;
+          ObjectPtr new_obj_;
         };
 
-        ResizeReferenceVisitor rrv(map);
+        ResizeReferenceVisitor rrv(old_obj_ptr, new_obj_ptr);
         if (ref->IsClass()) {
           // Class object native roots are the ArtField and ArtMethod 'declaring_class_' fields
           // which we don't want to be messing with as it would break ref-visitor assumptions about
@@ -1687,12 +1678,13 @@
       });
 }
 
-static void ReplaceStrongRoots(art::Thread* self, const ObjectMap& map)
+static void ReplaceStrongRoots(art::Thread* self, ObjectPtr old_obj_ptr, ObjectPtr new_obj_ptr)
     REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
   // replace root references expcept java frames.
   struct ResizeRootVisitor : public art::RootVisitor {
    public:
-    explicit ResizeRootVisitor(const ObjectMap& map) : map_(map) {}
+    ResizeRootVisitor(ObjectPtr new_val, ObjectPtr old_val)
+        : new_val_(new_val), old_val_(old_val) {}
 
     // TODO It's somewhat annoying to have to have this function implemented twice. It might be
     // good/useful to implement operator= for CompressedReference to allow us to use a template to
@@ -1701,8 +1693,7 @@
         REQUIRES_SHARED(art::Locks::mutator_lock_) {
       art::mirror::Object*** end = roots + count;
       for (art::mirror::Object** obj = *roots; roots != end; obj = *(++roots)) {
-        auto it = map_.find(*obj);
-        if (it != map_.end()) {
+        if (*obj == old_val_) {
           // Java frames might have the JIT doing optimizations (for example loop-unrolling or
           // eliding bounds checks) so we need deopt them once we're done here.
           if (info.GetType() == art::RootType::kRootJavaFrame) {
@@ -1717,7 +1708,7 @@
               threads_with_roots_.insert(info.GetThreadId());
             }
           }
-          *obj = it->second.Ptr();
+          *obj = new_val_.Ptr();
         }
       }
     }
@@ -1728,8 +1719,7 @@
       art::mirror::CompressedReference<art::mirror::Object>** end = roots + count;
       for (art::mirror::CompressedReference<art::mirror::Object>* obj = *roots; roots != end;
            obj = *(++roots)) {
-        auto it = map_.find(obj->AsMirrorPtr());
-        if (it != map_.end()) {
+        if (obj->AsMirrorPtr() == old_val_) {
           // Java frames might have the JIT doing optimizations (for example loop-unrolling or
           // eliding bounds checks) so we need deopt them once we're done here.
           if (info.GetType() == art::RootType::kRootJavaFrame) {
@@ -1744,7 +1734,7 @@
               threads_with_roots_.insert(info.GetThreadId());
             }
           }
-          obj->Assign(it->second);
+          obj->Assign(new_val_);
         }
       }
     }
@@ -1754,10 +1744,11 @@
     }
 
    private:
-    const ObjectMap& map_;
+    ObjectPtr new_val_;
+    ObjectPtr old_val_;
     std::unordered_set<uint32_t> threads_with_roots_;
   };
-  ResizeRootVisitor rrv(map);
+  ResizeRootVisitor rrv(new_obj_ptr, old_obj_ptr);
   art::Runtime::Current()->VisitRoots(&rrv, art::VisitRootFlags::kVisitRootFlagAllRoots);
   // Handle java Frames. Annoyingly the JIT can embed information about the length of the array into
   // the compiled code. By changing the length of the array we potentially invalidate these
@@ -1782,7 +1773,8 @@
 
 static void ReplaceWeakRoots(art::Thread* self,
                              EventHandler* event_handler,
-                             const ObjectMap& map)
+                             ObjectPtr old_obj_ptr,
+                             ObjectPtr new_obj_ptr)
     REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) {
   // Handle tags. We want to do this seprately from other weak-refs (handled below) because we need
   // to send additional events and handle cases where the agent might have tagged the new
@@ -1794,33 +1786,25 @@
   // situations where the order of weak-ref visiting affects the final tagging state. Since we have
   // the mutator_lock_ and gc-paused throughout this whole process no threads should be able to see
   // the interval where the objects are not tagged.
-  struct NewTagValue {
-   public:
-    ObjectPtr obsolete_obj_;
-    jlong obsolete_tag_;
-    ObjectPtr new_obj_;
-    jlong new_tag_;
-  };
-
-  // Map from the environment to the list of <obsolete_tag, new_tag> pairs that were changed.
-  std::unordered_map<ArtJvmTiEnv*, std::vector<NewTagValue>> changed_tags;
+  std::unordered_map<ArtJvmTiEnv*, jlong> obsolete_tags;
+  std::unordered_map<ArtJvmTiEnv*, jlong> non_obsolete_tags;
   event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) {
     // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it.
     art::Locks::mutator_lock_->AssertExclusiveHeld(self);
     env->object_tag_table->Lock();
     // Get the tags and clear them (so we don't need to special-case the normal weak-ref visitor)
-    for (auto it : map) {
-      jlong new_tag = 0;
-      jlong obsolete_tag = 0;
-      bool had_obsolete_tag = env->object_tag_table->RemoveLocked(it.first, &obsolete_tag);
-      bool had_new_tag = env->object_tag_table->RemoveLocked(it.second, &new_tag);
-      // Dispatch event.
-      if (had_obsolete_tag || had_new_tag) {
-        event_handler->DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>(
-            env, self, &obsolete_tag, &new_tag);
-        changed_tags.try_emplace(env).first->second.push_back(
-            { it.first, obsolete_tag, it.second, new_tag });
-      }
+    jlong new_tag = 0;
+    jlong obsolete_tag = 0;
+    bool had_new_tag = env->object_tag_table->RemoveLocked(new_obj_ptr, &new_tag);
+    bool had_obsolete_tag = env->object_tag_table->RemoveLocked(old_obj_ptr, &obsolete_tag);
+    // Dispatch event.
+    if (had_obsolete_tag || had_new_tag) {
+      event_handler->DispatchEventOnEnv<ArtJvmtiEvent::kObsoleteObjectCreated>(env,
+                                                                               self,
+                                                                               &obsolete_tag,
+                                                                               &new_tag);
+      obsolete_tags[env] = obsolete_tag;
+      non_obsolete_tags[env] = new_tag;
     }
     // After weak-ref update we need to go back and re-add obsoletes. We wait to avoid having to
     // deal with the visit-weaks overwriting the initial new_obj_ptr tag and generally making things
@@ -1830,34 +1814,34 @@
   // Handle weak-refs.
   struct ReplaceWeaksVisitor : public art::IsMarkedVisitor {
    public:
-    ReplaceWeaksVisitor(const ObjectMap& map) : map_(map) {}
+    ReplaceWeaksVisitor(ObjectPtr old_obj, ObjectPtr new_obj)
+        : old_obj_(old_obj), new_obj_(new_obj) {}
 
     art::mirror::Object* IsMarked(art::mirror::Object* obj)
         REQUIRES_SHARED(art::Locks::mutator_lock_) {
-      auto it = map_.find(obj);
-      if (it != map_.end()) {
-        return it->second.Ptr();
+      if (obj == old_obj_) {
+        return new_obj_.Ptr();
       } else {
         return obj;
       }
     }
 
    private:
-    const ObjectMap& map_;
+    ObjectPtr old_obj_;
+    ObjectPtr new_obj_;
   };
-  ReplaceWeaksVisitor rwv(map);
+  ReplaceWeaksVisitor rwv(old_obj_ptr, new_obj_ptr);
   art::Runtime::Current()->SweepSystemWeaks(&rwv);
   // Re-add the object tags. At this point all weak-references to the old_obj_ptr are gone.
   event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) {
     // Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it.
     art::Locks::mutator_lock_->AssertExclusiveHeld(self);
     env->object_tag_table->Lock();
-    auto it = changed_tags.find(env);
-    if (it != changed_tags.end()) {
-      for (const NewTagValue& v : it->second) {
-        env->object_tag_table->SetLocked(v.obsolete_obj_, v.obsolete_tag_);
-        env->object_tag_table->SetLocked(v.new_obj_, v.new_tag_);
-      }
+    if (obsolete_tags.find(env) != obsolete_tags.end()) {
+      env->object_tag_table->SetLocked(old_obj_ptr, obsolete_tags[env]);
+    }
+    if (non_obsolete_tags.find(env) != non_obsolete_tags.end()) {
+      env->object_tag_table->SetLocked(new_obj_ptr, non_obsolete_tags[env]);
     }
     env->object_tag_table->Unlock();
   });
@@ -1868,14 +1852,9 @@
 void HeapExtensions::ReplaceReference(art::Thread* self,
                                       art::ObjPtr<art::mirror::Object> old_obj_ptr,
                                       art::ObjPtr<art::mirror::Object> new_obj_ptr) {
-  ObjectMap map { { old_obj_ptr, new_obj_ptr } };
-  ReplaceReferences(self, map);
-}
-
-void HeapExtensions::ReplaceReferences(art::Thread* self, const ObjectMap& map) {
-  ReplaceObjectReferences(map);
-  ReplaceStrongRoots(self, map);
-  ReplaceWeakRoots(self, HeapExtensions::gEventHandler, map);
+  ReplaceObjectReferences(old_obj_ptr, new_obj_ptr);
+  ReplaceStrongRoots(self, old_obj_ptr, new_obj_ptr);
+  ReplaceWeakRoots(self, HeapExtensions::gEventHandler, old_obj_ptr, new_obj_ptr);
 }
 
 jvmtiError HeapExtensions::ChangeArraySize(jvmtiEnv* env, jobject arr, jsize new_size) {
diff --git a/openjdkjvmti/ti_heap.h b/openjdkjvmti/ti_heap.h
index ee8b4d6..2e27cc7 100644
--- a/openjdkjvmti/ti_heap.h
+++ b/openjdkjvmti/ti_heap.h
@@ -17,8 +17,6 @@
 #ifndef ART_OPENJDKJVMTI_TI_HEAP_H_
 #define ART_OPENJDKJVMTI_TI_HEAP_H_
 
-#include <unordered_map>
-
 #include "jvmti.h"
 
 #include "base/locks.h"
@@ -26,7 +24,6 @@
 namespace art {
 class Thread;
 template<typename T> class ObjPtr;
-class HashObjPtr;
 namespace mirror {
 class Object;
 }  // namespace mirror
@@ -91,13 +88,6 @@
 
   static jvmtiError JNICALL ChangeArraySize(jvmtiEnv* env, jobject arr, jsize new_size);
 
-  static void ReplaceReferences(
-      art::Thread* self,
-      const std::unordered_map<art::ObjPtr<art::mirror::Object>,
-                               art::ObjPtr<art::mirror::Object>,
-                               art::HashObjPtr>& refs)
-        REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_);
-
   static void ReplaceReference(art::Thread* self,
                                art::ObjPtr<art::mirror::Object> original,
                                art::ObjPtr<art::mirror::Object> replacement)
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index ebbe6ac..87080ca 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -42,8 +42,6 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
-#include "alloc_manager.h"
-#include "android-base/macros.h"
 #include "android-base/thread_annotations.h"
 #include "art_field-inl.h"
 #include "art_field.h"
@@ -117,7 +115,6 @@
 #include "reflective_value_visitor.h"
 #include "runtime.h"
 #include "runtime_globals.h"
-#include "scoped_thread_state_change.h"
 #include "stack.h"
 #include "thread.h"
 #include "thread_list.h"
@@ -463,33 +460,30 @@
     }
     // Check for already existing non-static fields/methods.
     // TODO Remove this once we support generic method/field addition.
-    if (!klass->IsFinal()) {
-      bool non_static_method = false;
-      klass->VisitMethods([&](art::ArtMethod* m) REQUIRES_SHARED(art::Locks::mutator_lock_) {
-        // Since direct-methods (ie privates + <init> are not in any vtable/iftable we can update
-        // them).
-        if (!m->IsDirect()) {
-          non_static_method = true;
-          *error_msg = StringPrintf("%s has a non-direct function %s",
-                                    klass->PrettyClass().c_str(),
-                                    m->PrettyMethod().c_str());
-        }
-      }, art::kRuntimePointerSize);
-      if (non_static_method) {
-        return ERR(UNMODIFIABLE_CLASS);
+    bool non_static_method = false;
+    klass->VisitMethods([&](art::ArtMethod* m) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+      // Since direct-methods (ie privates + <init> are not in any vtable/iftable we can update
+      // them).
+      if (!m->IsDirect()) {
+        non_static_method = true;
+        *error_msg = StringPrintf("%s has a non-direct function %s",
+                                  klass->PrettyClass().c_str(),
+                                  m->PrettyMethod().c_str());
       }
-      bool non_static_field = false;
-      klass->VisitFields([&](art::ArtField* f) REQUIRES_SHARED(art::Locks::mutator_lock_) {
-        if (!f->IsStatic()) {
-          non_static_field = true;
-          *error_msg = StringPrintf("%s has a non-static field %s",
-                                    klass->PrettyClass().c_str(),
-                                    f->PrettyField().c_str());
-        }
-      });
-      if (non_static_field) {
-        return ERR(UNMODIFIABLE_CLASS);
+    }, art::kRuntimePointerSize);
+    if (non_static_method) {
+      return ERR(UNMODIFIABLE_CLASS);
+    }
+    bool non_static_field = false;
+    klass->VisitFields([&](art::ArtField* f) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+      if (!f->IsStatic()) {
+        non_static_field = true;
+        *error_msg = StringPrintf(
+            "%s has a non-static field %s", klass->PrettyClass().c_str(), f->PrettyField().c_str());
       }
+    });
+    if (non_static_field) {
+      return ERR(UNMODIFIABLE_CLASS);
     }
     // Check for fields/methods which were returned before moving to index jni id type.
     // TODO We might want to rework how this is done. Once full redefinition is implemented we will
@@ -991,12 +985,9 @@
           return old_method_id == new_method_id;
         });
 
-    if (!new_method.IsStaticOrDirect()) {
-      RecordHasVirtualMembers();
-    }
     if (old_iter == old_methods.cend()) {
       // TODO Support adding non-static methods.
-      if (is_structural && (new_method.IsStaticOrDirect() || h_klass->IsFinal())) {
+      if (is_structural && new_method.IsStaticOrDirect()) {
         RecordNewMethodAdded();
       } else {
         RecordFailure(
@@ -1055,12 +1046,9 @@
           FieldNameAndSignature old_field_id(&old_dex_file, old_iter.GetIndex());
           return old_field_id == new_field_id;
         });
-    if (!new_field.IsStatic()) {
-      RecordHasVirtualMembers();
-    }
     if (old_iter == old_fields.cend()) {
       // TODO Support adding non-static fields.
-      if (driver_->IsStructuralRedefinition() && (new_field.IsStatic() || h_klass->IsFinal())) {
+      if (driver_->IsStructuralRedefinition() && new_field.IsStatic()) {
         RecordNewFieldAdded();
       } else {
         RecordFailure(ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
@@ -1181,10 +1169,6 @@
   jvmtiError res;
   if (driver_->type_ == RedefinitionType::kStructural && this->IsStructuralRedefinition()) {
     res = Redefiner::GetClassRedefinitionError<RedefinitionType::kStructural>(h_klass, &err);
-    if (res == OK && HasVirtualMembers() && h_klass->IsFinalizable()) {
-      res = ERR(INTERNAL);
-      err = "Cannot redefine finalizable objects at this time.";
-    }
   } else {
     res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(h_klass, &err);
   }
@@ -1217,11 +1201,9 @@
     kSlotOldObsoleteMethods = 6,
     kSlotOldDexCaches = 7,
     kSlotNewClassObject = 8,
-    kSlotOldInstanceObjects = 9,
-    kSlotNewInstanceObjects = 10,
 
     // Must be last one.
-    kNumSlots = 11,
+    kNumSlots = 9,
   };
 
   // This needs to have a HandleScope passed in that is capable of creating a new Handle without
@@ -1287,18 +1269,6 @@
     return art::ObjPtr<art::mirror::Class>::DownCast(GetSlot(klass_index, kSlotNewClassObject));
   }
 
-  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetOldInstanceObjects(
-      jint klass_index) const REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    return art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>>::DownCast(
-        GetSlot(klass_index, kSlotOldInstanceObjects));
-  }
-
-  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetNewInstanceObjects(
-      jint klass_index) const REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    return art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>>::DownCast(
-        GetSlot(klass_index, kSlotNewInstanceObjects));
-  }
-
   void SetSourceClassLoader(jint klass_index, art::ObjPtr<art::mirror::ClassLoader> loader)
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
     SetSlot(klass_index, kSlotSourceClassLoader, loader);
@@ -1338,16 +1308,6 @@
     SetSlot(klass_index, kSlotNewClassObject, klass);
   }
 
-  void SetOldInstanceObjects(jint klass_index,
-                             art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> objs)
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    SetSlot(klass_index, kSlotOldInstanceObjects, objs);
-  }
-  void SetNewInstanceObjects(jint klass_index,
-                             art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> objs)
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    SetSlot(klass_index, kSlotNewInstanceObjects, objs);
-  }
   int32_t Length() const REQUIRES_SHARED(art::Locks::mutator_lock_) {
     return arr_->GetLength() / kNumSlots;
   }
@@ -1432,11 +1392,6 @@
     return *this;
   }
 
-  // Compat for STL iterators.
-  RedefinitionDataIter& operator*() {
-    return *this;
-  }
-
   Redefiner::ClassRedefinition& GetRedefinition() REQUIRES_SHARED(art::Locks::mutator_lock_) {
     return (*holder_.GetRedefinitions())[idx_];
   }
@@ -1483,14 +1438,6 @@
     return holder_.GetNewClassObject(idx_);
   }
 
-  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetOldInstanceObjects() const
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    return holder_.GetOldInstanceObjects(idx_);
-  }
-  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> GetNewInstanceObjects() const
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    return holder_.GetNewInstanceObjects(idx_);
-  }
   int32_t GetIndex() const {
     return idx_;
   }
@@ -1531,14 +1478,6 @@
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
     holder_.SetNewClassObject(idx_, klass);
   }
-  void SetOldInstanceObjects(art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> objs)
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    holder_.SetOldInstanceObjects(idx_, objs);
-  }
-  void SetNewInstanceObjects(art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> objs)
-      REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    holder_.SetNewInstanceObjects(idx_, objs);
-  }
 
  private:
   int32_t idx_;
@@ -1641,75 +1580,6 @@
   return true;
 }
 
-bool Redefiner::ClassRedefinition::CollectAndCreateNewInstances(
-    /*out*/ RedefinitionDataIter* cur_data) {
-  if (!IsStructuralRedefinition()) {
-    return true;
-  }
-  art::VariableSizedHandleScope hs(driver_->self_);
-  art::Handle<art::mirror::Class> old_klass(hs.NewHandle(cur_data->GetMirrorClass()));
-  std::vector<art::Handle<art::mirror::Object>> old_instances;
-  art::gc::Heap* heap = driver_->runtime_->GetHeap();
-  auto is_instance = [&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    if (HasVirtualMembers()) {
-      return old_klass->IsAssignableFrom(obj->GetClass());
-    } else {
-      // We don't need to deal with objects of subtypes when we don't modify virtuals since the
-      // vtable + field layout will remain the same.
-      return old_klass.Get() == obj->GetClass();
-    }
-  };
-  heap->VisitObjects([&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    if (is_instance(obj)) {
-      CHECK(old_klass.Get() == obj->GetClass()) << "No support for subtypes yet!";
-      old_instances.push_back(hs.NewHandle(obj));
-    }
-  });
-  VLOG(plugin) << "Collected " << old_instances.size() << " instances to recreate!";
-
-  art::Handle<art::mirror::Class> obj_array_class(
-      hs.NewHandle(art::GetClassRoot<art::mirror::ObjectArray<art::mirror::Object>>(
-          driver_->runtime_->GetClassLinker())));
-  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> old_instances_arr(
-      hs.NewHandle(art::mirror::ObjectArray<art::mirror::Object>::Alloc(
-          driver_->self_, obj_array_class.Get(), old_instances.size())));
-  if (old_instances_arr.IsNull()) {
-    driver_->self_->AssertPendingOOMException();
-    driver_->self_->ClearException();
-    RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate old_instance arrays!");
-    return false;
-  }
-  for (uint32_t i = 0; i < old_instances.size(); ++i) {
-    old_instances_arr->Set(i, old_instances[i].Get());
-  }
-  cur_data->SetOldInstanceObjects(old_instances_arr.Get());
-
-  art::Handle<art::mirror::ObjectArray<art::mirror::Object>> new_instances_arr(
-      hs.NewHandle(art::mirror::ObjectArray<art::mirror::Object>::Alloc(
-          driver_->self_, obj_array_class.Get(), old_instances.size())));
-  if (new_instances_arr.IsNull()) {
-    driver_->self_->AssertPendingOOMException();
-    driver_->self_->ClearException();
-    RecordFailure(ERR(OUT_OF_MEMORY), "Could not allocate new_instance arrays!");
-    return false;
-  }
-  art::Handle<art::mirror::Class> new_klass(hs.NewHandle(cur_data->GetNewClassObject()));
-  for (uint32_t i = 0; i < old_instances.size(); ++i) {
-    art::ObjPtr<art::mirror::Object> new_instance(new_klass->AllocObject(driver_->self_));
-    if (new_instance.IsNull()) {
-      driver_->self_->AssertPendingOOMException();
-      driver_->self_->ClearException();
-      std::string msg(
-          StringPrintf("Could not allocate instance %d of %zu", i, old_instances.size()));
-      RecordFailure(ERR(OUT_OF_MEMORY), msg);
-      return false;
-    }
-    new_instances_arr->Set(i, new_instance);
-  }
-  cur_data->SetNewInstanceObjects(new_instances_arr.Get());
-  return true;
-}
-
 bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
     /*out*/RedefinitionDataIter* cur_data) {
   art::ScopedObjectAccessUnchecked soa(driver_->self_);
@@ -1931,16 +1801,6 @@
   return true;
 }
 
-bool Redefiner::CollectAndCreateNewInstances(RedefinitionDataHolder& holder) {
-  for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
-    // Allocate the data this redefinition requires.
-    if (!data.GetRedefinition().CollectAndCreateNewInstances(&data)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 bool Redefiner::FinishAllRemainingAllocations(RedefinitionDataHolder& holder) {
   for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
     // Allocate the data this redefinition requires.
@@ -1989,36 +1849,6 @@
   art::Thread* self_;
 };
 
-class ScopedSuspendAllocations {
- public:
-  ScopedSuspendAllocations(art::Runtime* runtime, RedefinitionDataHolder& h)
-      REQUIRES_SHARED(art::Locks::mutator_lock_)
-      : paused_(false) {
-    if (std::any_of(h.begin(),
-                    h.end(),
-                    [](auto r) REQUIRES_SHARED(art::Locks::mutator_lock_) {
-                      return r.GetRedefinition().IsStructuralRedefinition();
-                    })) {
-      VLOG(plugin) << "Pausing allocations for structural redefinition.";
-      paused_ = true;
-      AllocationManager::Get()->PauseAllocations(art::Thread::Current());
-      // Collect garbage so we don't need to recreate as much.
-      runtime->GetHeap()->CollectGarbage(/*clear_soft_references=*/false);
-    }
-  }
-
-  ~ScopedSuspendAllocations() REQUIRES_SHARED(art::Locks::mutator_lock_) {
-    if (paused_) {
-      AllocationManager::Get()->ResumeAllocations(art::Thread::Current());
-    }
-  }
-
- private:
-  bool paused_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedSuspendAllocations);
-};
-
 jvmtiError Redefiner::Run() {
   art::StackHandleScope<1> hs(self_);
   // Allocate an array to hold onto all java temporary objects associated with this redefinition.
@@ -2043,11 +1873,6 @@
     return result_;
   }
 
-  ScopedSuspendAllocations suspend_alloc(runtime_, holder);
-  if (!CollectAndCreateNewInstances(holder)) {
-    return result_;
-  }
-
   // At this point we can no longer fail without corrupting the runtime state.
   for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
     art::ClassLinker* cl = runtime_->GetClassLinker();
@@ -2198,120 +2023,6 @@
   }
 }
 
-static void CopyField(art::ObjPtr<art::mirror::Object> target,
-                      art::ArtField* new_field,
-                      art::ObjPtr<art::mirror::Object> source,
-                      art::ArtField& old_field) REQUIRES(art::Locks::mutator_lock_) {
-  art::Primitive::Type ftype = old_field.GetTypeAsPrimitiveType();
-  CHECK_EQ(ftype, new_field->GetTypeAsPrimitiveType())
-      << old_field.PrettyField() << " vs " << new_field->PrettyField();
-  if (ftype == art::Primitive::kPrimNot) {
-    new_field->SetObject<false>(target, old_field.GetObject(source));
-  } else {
-    switch (ftype) {
-#define UPDATE_FIELD(TYPE)                                            \
-  case art::Primitive::kPrim##TYPE:                                   \
-    new_field->Set##TYPE<false>(target, old_field.Get##TYPE(source)); \
-    break
-      UPDATE_FIELD(Int);
-      UPDATE_FIELD(Float);
-      UPDATE_FIELD(Long);
-      UPDATE_FIELD(Double);
-      UPDATE_FIELD(Short);
-      UPDATE_FIELD(Char);
-      UPDATE_FIELD(Byte);
-      UPDATE_FIELD(Boolean);
-      case art::Primitive::kPrimNot:
-      case art::Primitive::kPrimVoid:
-        LOG(FATAL) << "Unexpected field with type " << ftype << " found!";
-        UNREACHABLE();
-#undef UPDATE_FIELD
-    }
-  }
-}
-
-static void CopyFields(bool is_static,
-                       art::ObjPtr<art::mirror::Object> target,
-                       art::ObjPtr<art::mirror::Class> target_class,
-                       art::ObjPtr<art::mirror::Object> source,
-                       art::ObjPtr<art::mirror::Class> source_class)
-    REQUIRES(art::Locks::mutator_lock_) {
-  DCHECK(!source_class->IsObjectClass() && !target_class->IsObjectClass())
-      << "Should not be overriding object class fields. Target: " << target_class->PrettyClass()
-      << " Source: " << source_class->PrettyClass();
-  for (art::ArtField& f : (is_static ? source_class->GetSFields() : source_class->GetIFields())) {
-    art::ArtField* new_field =
-        (is_static ? target_class->FindDeclaredStaticField(f.GetName(), f.GetTypeDescriptor())
-                   : target_class->FindDeclaredInstanceField(f.GetName(), f.GetTypeDescriptor()));
-    CHECK(new_field != nullptr) << "could not find new version of " << f.PrettyField();
-    CopyField(target, new_field, source, f);
-  }
-  if (!is_static && !target_class->GetSuperClass()->IsObjectClass()) {
-    CopyFields(
-        is_static, target, target_class->GetSuperClass(), source, source_class->GetSuperClass());
-  }
-}
-
-static void ClearField(art::ObjPtr<art::mirror::Object> target, art::ArtField& field)
-    REQUIRES(art::Locks::mutator_lock_) {
-  art::Primitive::Type ftype = field.GetTypeAsPrimitiveType();
-  if (ftype == art::Primitive::kPrimNot) {
-    field.SetObject<false>(target, nullptr);
-  } else {
-    switch (ftype) {
-#define UPDATE_FIELD(TYPE)             \
-  case art::Primitive::kPrim##TYPE:    \
-    field.Set##TYPE<false>(target, 0); \
-    break
-      UPDATE_FIELD(Int);
-      UPDATE_FIELD(Float);
-      UPDATE_FIELD(Long);
-      UPDATE_FIELD(Double);
-      UPDATE_FIELD(Short);
-      UPDATE_FIELD(Char);
-      UPDATE_FIELD(Byte);
-      UPDATE_FIELD(Boolean);
-      case art::Primitive::kPrimNot:
-      case art::Primitive::kPrimVoid:
-        LOG(FATAL) << "Unexpected field with type " << ftype << " found!";
-        UNREACHABLE();
-#undef UPDATE_FIELD
-    }
-  }
-}
-
-static void ClearFields(bool is_static,
-                        art::ObjPtr<art::mirror::Object> target,
-                        art::ObjPtr<art::mirror::Class> target_class)
-    REQUIRES(art::Locks::mutator_lock_) {
-  DCHECK(!target_class->IsObjectClass());
-  for (art::ArtField& f : (is_static ? target_class->GetSFields() : target_class->GetIFields())) {
-    ClearField(target, f);
-  }
-  if (!is_static && !target_class->GetSuperClass()->IsObjectClass()) {
-    ClearFields(is_static, target, target_class->GetSuperClass());
-  }
-}
-
-static void CopyAndClearFields(bool is_static,
-                               art::ObjPtr<art::mirror::Object> target,
-                               art::ObjPtr<art::mirror::Class> target_class,
-                               art::ObjPtr<art::mirror::Object> source,
-                               art::ObjPtr<art::mirror::Class> source_class)
-    REQUIRES(art::Locks::mutator_lock_) {
-  // Copy all non-j.l.Object fields
-  CopyFields(is_static, target, target_class, source, source_class);
-  // Copy the lock-word.
-  target->SetLockWord(source->GetLockWord(false), false);
-  // Clear (reset) the old one.
-  source->SetLockWord(art::LockWord::Default(), false);
-  art::WriteBarrier::ForEveryFieldWrite(target);
-
-  // Clear the fields from the old class. We don't need it anymore.
-  ClearFields(is_static, source, source_class);
-  art::WriteBarrier::ForEveryFieldWrite(source);
-}
-
 void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDataIter& holder) {
   DCHECK(IsStructuralRedefinition());
   // LETS GO. We've got all new class structures so no need to do all the updating of the stacks.
@@ -2325,24 +2036,40 @@
   std::map<art::ArtMethod*, art::ArtMethod*> method_map;
   std::map<art::ArtField*, art::ArtField*> field_map;
   CollectNewFieldAndMethodMappings(holder, &method_map, &field_map);
-  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> new_instances(
-      holder.GetNewInstanceObjects());
-  art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> old_instances(
-      holder.GetOldInstanceObjects());
+  // Copy over the fields of the object.
   CHECK(!orig.IsNull());
   CHECK(!replacement.IsNull());
-  // Copy over the static fields of the class and all the instance fields.
-  CopyAndClearFields(/*is_static=*/true, replacement, replacement, orig, orig);
+  for (art::ArtField& f : orig->GetSFields()) {
+    art::ArtField* new_field =
+        replacement->FindDeclaredStaticField(f.GetName(), f.GetTypeDescriptor());
+    CHECK(new_field != nullptr) << "could not find new version of " << f.PrettyField();
+    art::Primitive::Type ftype = f.GetTypeAsPrimitiveType();
+    CHECK_EQ(ftype, new_field->GetTypeAsPrimitiveType())
+        << f.PrettyField() << " vs " << new_field->PrettyField();
+    if (ftype == art::Primitive::kPrimNot) {
+      new_field->SetObject<false>(replacement, f.GetObject(orig));
+    } else {
+      switch (ftype) {
+#define UPDATE_FIELD(TYPE)                                       \
+  case art::Primitive::kPrim##TYPE:                              \
+    new_field->Set##TYPE<false>(replacement, f.Get##TYPE(orig)); \
+    break
 
-  // Copy and clear the fields of the old-instances.
-  for (int32_t i = 0; i < old_instances->GetLength(); i++) {
-    art::ObjPtr<art::mirror::Object> old_instance(old_instances->Get(i));
-    art::ObjPtr<art::mirror::Object> new_instance(new_instances->Get(i));
-    CopyAndClearFields(/*is_static=*/false,
-                       new_instance,
-                       new_instance->GetClass(),
-                       old_instance,
-                       old_instance->GetClass());
+        UPDATE_FIELD(Int);
+        UPDATE_FIELD(Float);
+        UPDATE_FIELD(Long);
+        UPDATE_FIELD(Double);
+        UPDATE_FIELD(Short);
+        UPDATE_FIELD(Char);
+        UPDATE_FIELD(Byte);
+        UPDATE_FIELD(Boolean);
+        case art::Primitive::kPrimNot:
+        case art::Primitive::kPrimVoid:
+          LOG(FATAL) << "Unexpected field with type " << ftype << " found!";
+          UNREACHABLE();
+#undef UPDATE_FIELD
+      }
+    }
   }
   // Mark old class obsolete.
   orig->SetObsoleteObject();
@@ -2352,6 +2079,9 @@
     m.SetDontCompile();
     DCHECK_EQ(orig, m.GetDeclaringClass());
   }
+  // Copy the lock-word
+  replacement->SetLockWord(orig->GetLockWord(false), false);
+  orig->SetLockWord(art::LockWord::Default(), false);
   // Update live pointers in ART code.
   auto could_change_resolution_of = [&](auto* field_or_method,
                                         const auto& info) REQUIRES(art::Locks::mutator_lock_) {
@@ -2436,23 +2166,39 @@
   // Force every frame of every thread to deoptimize (any frame might have eg offsets compiled in).
   driver_->runtime_->GetInstrumentation()->DeoptimizeAllThreadFrames();
 
-  std::unordered_map<art::ObjPtr<art::mirror::Object>,
-                     art::ObjPtr<art::mirror::Object>,
-                     art::HashObjPtr> map;
-  map.emplace(orig, replacement);
-  for (int32_t i = 0; i < old_instances->GetLength(); i++) {
-    map.emplace(old_instances->Get(i), new_instances->Get(i));
-  }
-
-  // Actually perform the general replacement. This doesn't affect ArtMethod/ArtFields. It does
-  // affect the declaring_class field of all the obsolete objects, which is unfortunate and needs to
-  // be undone. This replaces the mirror::Class in 'holder' as well. It's magic!
-  HeapExtensions::ReplaceReferences(driver_->self_, map);
+  // Actually perform the general replacement. This doesn't affect ArtMethod/ArtFields.
+  // This replaces the mirror::Class in 'holder' as well. It's magic!
+  HeapExtensions::ReplaceReference(driver_->self_, orig, replacement);
 
   // Save the old class so that the JIT gc doesn't get confused by it being collected before the
   // jit code. This is also needed to keep the dex-caches of any obsolete methods live.
   replacement->GetExtData()->SetObsoleteClass(orig);
 
+  // Clear the static fields of the old-class.
+  for (art::ArtField& f : orig->GetSFields()) {
+    switch (f.GetTypeAsPrimitiveType()) {
+    #define UPDATE_FIELD(TYPE)            \
+      case art::Primitive::kPrim ## TYPE: \
+        f.Set ## TYPE <false>(orig, 0);   \
+        break
+
+      UPDATE_FIELD(Int);
+      UPDATE_FIELD(Float);
+      UPDATE_FIELD(Long);
+      UPDATE_FIELD(Double);
+      UPDATE_FIELD(Short);
+      UPDATE_FIELD(Char);
+      UPDATE_FIELD(Byte);
+      UPDATE_FIELD(Boolean);
+      case art::Primitive::kPrimNot:
+        f.SetObject<false>(orig, nullptr);
+        break;
+      case art::Primitive::kPrimVoid:
+        LOG(FATAL) << "Unexpected field with type void found!";
+        UNREACHABLE();
+    #undef UPDATE_FIELD
+    }
+  }
 
   art::jit::Jit* jit = driver_->runtime_->GetJit();
   if (jit != nullptr) {
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index cedce92..58a688c 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -41,7 +41,6 @@
 #include "art_jvmti.h"
 #include "base/array_ref.h"
 #include "base/globals.h"
-#include "dex/class_accessor.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_structs.h"
 #include "jni/jni_env_ext-inl.h"
@@ -156,9 +155,6 @@
     bool FinishRemainingAllocations(/*out*/RedefinitionDataIter* cur_data)
         REQUIRES_SHARED(art::Locks::mutator_lock_);
 
-    bool CollectAndCreateNewInstances(/*out*/RedefinitionDataIter* cur_data)
-        REQUIRES_SHARED(art::Locks::mutator_lock_);
-
     bool AllocateAndRememberNewDexFileCookie(
         art::Handle<art::mirror::ClassLoader> source_class_loader,
         art::Handle<art::mirror::Object> dex_file_obj,
@@ -238,14 +234,8 @@
 
     void RecordNewMethodAdded();
     void RecordNewFieldAdded();
-    void RecordHasVirtualMembers() {
-      has_virtuals_ = true;
-    }
 
-    bool HasVirtualMembers() const {
-      return has_virtuals_;
-    }
-
+   private:
     bool IsStructuralRedefinition() const {
       DCHECK(!(added_fields_ || added_methods_) || driver_->IsStructuralRedefinition())
           << "added_fields_: " << added_fields_ << " added_methods_: " << added_methods_
@@ -253,7 +243,6 @@
       return driver_->IsStructuralRedefinition() && (added_fields_ || added_methods_);
     }
 
-   private:
     void UpdateClassStructurally(const RedefinitionDataIter& cur_data)
         REQUIRES(art::Locks::mutator_lock_);
 
@@ -268,7 +257,6 @@
 
     bool added_fields_ = false;
     bool added_methods_ = false;
-    bool has_virtuals_ = false;
 
     // Does the class need to be reverified due to verification soft-fails possibly forcing
     // interpreter or lock-counting?
@@ -323,8 +311,6 @@
       REQUIRES_SHARED(art::Locks::mutator_lock_);
   bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
       REQUIRES_SHARED(art::Locks::mutator_lock_);
-  bool CollectAndCreateNewInstances(RedefinitionDataHolder& holder)
-      REQUIRES_SHARED(art::Locks::mutator_lock_);
   void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_);
   void ReverifyClasses(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_);
   void UnregisterAllBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
index c3518f3..4b85df0 100644
--- a/runtime/base/locks.h
+++ b/runtime/base/locks.h
@@ -129,9 +129,6 @@
 
   kMutatorLock,
   kInstrumentEntrypointsLock,
-  // This is a generic lock level for a top-level lock meant to be gained after having the
-  // UserCodeSuspensionLock.
-  kPostUserCodeSuspensionTopLevelLock,
   kUserCodeSuspensionLock,
   kZygoteCreationLock,
 
diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h
index 376b524..a578252 100644
--- a/runtime/gc/allocation_listener.h
+++ b/runtime/gc/allocation_listener.h
@@ -23,13 +23,11 @@
 #include "base/locks.h"
 #include "base/macros.h"
 #include "gc_root.h"
-#include "handle.h"
 #include "obj_ptr.h"
 
 namespace art {
 
 namespace mirror {
-class Class;
 class Object;
 }  // namespace mirror
 
@@ -41,26 +39,6 @@
  public:
   virtual ~AllocationListener() {}
 
-  // An event to allow a listener to intercept and modify an allocation before it takes place.
-  // The listener can change the byte_count and type as they see fit. Extreme caution should be used
-  // when doing so. This can also be used to control allocation occurring on another thread.
-  //
-  // Concurrency guarantees: This might be called multiple times for each single allocation. It's
-  // guaranteed that, between the final call to the callback and the object being visible to
-  // heap-walks there are no suspensions. If a suspension was allowed between these events the
-  // callback will be invoked again after passing the suspend point.
-  //
-  // If the alloc succeeds it is guaranteed there are no suspend-points between the last return of
-  // PreObjectAlloc and the newly allocated object being visible to heap-walks.
-  //
-  // This can also be used to make any last-minute changes to the type or size of the allocation.
-  virtual void PreObjectAllocated(Thread* self ATTRIBUTE_UNUSED,
-                                  MutableHandle<mirror::Class> type ATTRIBUTE_UNUSED,
-                                  size_t* byte_count ATTRIBUTE_UNUSED)
-      REQUIRES(!Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_) {}
-  // Fast check if we want to get the PreObjectAllocated callback, to avoid the expense of creating
-  // handles. Defaults to false.
-  virtual bool HasPreAlloc() const { return false; }
   virtual void ObjectAllocated(Thread* self, ObjPtr<mirror::Object>* obj, size_t byte_count)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 };
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 04632ef..c1b3a63 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -65,30 +65,10 @@
     HandleWrapperObjPtr<mirror::Class> h = hs.NewHandleWrapper(&klass);
     self->PoisonObjectPointers();
   }
-  auto send_pre_object_allocated = [&]() REQUIRES_SHARED(Locks::mutator_lock_)
-                                         ACQUIRE(Roles::uninterruptible_) {
-    if constexpr (kInstrumented) {
-      AllocationListener* l = nullptr;
-      l = alloc_listener_.load(std::memory_order_seq_cst);
-      if (UNLIKELY(l != nullptr) && UNLIKELY(l->HasPreAlloc())) {
-        StackHandleScope<1> hs(self);
-        HandleWrapperObjPtr<mirror::Class> h_klass(hs.NewHandleWrapper(&klass));
-        l->PreObjectAllocated(self, h_klass, &byte_count);
-      }
-    }
-    return self->StartAssertNoThreadSuspension("Called PreObjectAllocated, no suspend until alloc");
-  };
-  // Do the initial pre-alloc
-  const char* old_cause = send_pre_object_allocated();
-  // We shouldn't have any NoThreadSuspension here!
-  DCHECK(old_cause == nullptr) << old_cause;
-
   // Need to check that we aren't the large object allocator since the large object allocation code
   // path includes this function. If we didn't check we would have an infinite loop.
   ObjPtr<mirror::Object> obj;
   if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
-    // AllocLargeObject can suspend and will recall PreObjectAllocated if needed.
-    self->EndAssertNoThreadSuspension(old_cause);
     obj = AllocLargeObject<kInstrumented, PreFenceVisitor>(self, &klass, byte_count,
                                                            pre_fence_visitor);
     if (obj != nullptr) {
@@ -100,8 +80,6 @@
     // If the large object allocation failed, try to use the normal spaces (main space,
     // non moving space). This can happen if there is significant virtual address space
     // fragmentation.
-    // We need to send the PreObjectAllocated again, we might have suspended during our failure.
-    old_cause = send_pre_object_allocated();
   }
   // bytes allocated for the (individual) object.
   size_t bytes_allocated;
@@ -122,7 +100,6 @@
     usable_size = bytes_allocated;
     no_suspend_pre_fence_visitor(obj, usable_size);
     QuasiAtomic::ThreadFenceForConstructor();
-    self->EndAssertNoThreadSuspension(old_cause);
   } else if (
       !kInstrumented && allocator == kAllocatorTypeRosAlloc &&
       (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) != nullptr &&
@@ -135,7 +112,6 @@
     usable_size = bytes_allocated;
     no_suspend_pre_fence_visitor(obj, usable_size);
     QuasiAtomic::ThreadFenceForConstructor();
-    self->EndAssertNoThreadSuspension(old_cause);
   } else {
     // Bytes allocated that includes bulk thread-local buffer allocations in addition to direct
     // non-TLAB object allocations.
@@ -145,19 +121,14 @@
     if (UNLIKELY(obj == nullptr)) {
       // AllocateInternalWithGc can cause thread suspension, if someone instruments the entrypoints
       // or changes the allocator in a suspend point here, we need to retry the allocation.
-      // It will send the pre-alloc event again.
-      self->EndAssertNoThreadSuspension(old_cause);
       obj = AllocateInternalWithGc(self,
                                    allocator,
                                    kInstrumented,
                                    byte_count,
                                    &bytes_allocated,
                                    &usable_size,
-                                   &bytes_tl_bulk_allocated,
-                                   &klass,
-                                   &old_cause);
+                                   &bytes_tl_bulk_allocated, &klass);
       if (obj == nullptr) {
-        self->EndAssertNoThreadSuspension(old_cause);
         // The only way that we can get a null return if there is no pending exception is if the
         // allocator or instrumentation changed.
         if (!self->IsExceptionPending()) {
@@ -185,7 +156,6 @@
     }
     no_suspend_pre_fence_visitor(obj, usable_size);
     QuasiAtomic::ThreadFenceForConstructor();
-    self->EndAssertNoThreadSuspension(old_cause);
     if (bytes_tl_bulk_allocated > 0) {
       size_t num_bytes_allocated_before =
           num_bytes_allocated_.fetch_add(bytes_tl_bulk_allocated, std::memory_order_relaxed);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index a97ff98..85b79da 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -17,7 +17,6 @@
 #include "heap.h"
 
 #include <limits>
-#include "android-base/thread_annotations.h"
 #if defined(__BIONIC__) || defined(__GLIBC__)
 #include <malloc.h>  // For mallinfo()
 #endif
@@ -1724,37 +1723,11 @@
                                              size_t* bytes_allocated,
                                              size_t* usable_size,
                                              size_t* bytes_tl_bulk_allocated,
-                                             ObjPtr<mirror::Class>* klass,
-                                             /*out*/const char** old_no_thread_suspend_cause) {
+                                             ObjPtr<mirror::Class>* klass) {
   bool was_default_allocator = allocator == GetCurrentAllocator();
   // Make sure there is no pending exception since we may need to throw an OOME.
   self->AssertNoPendingException();
   DCHECK(klass != nullptr);
-  auto release_no_suspend = [&]() RELEASE(Roles::uninterruptible_) {
-    self->EndAssertNoThreadSuspension(*old_no_thread_suspend_cause);
-  };
-  auto send_object_pre_alloc = [&]() ACQUIRE(Roles::uninterruptible_)
-                                     REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (UNLIKELY(instrumented)) {
-    AllocationListener* l = nullptr;
-    l = alloc_listener_.load(std::memory_order_seq_cst);
-    if (UNLIKELY(l != nullptr) && UNLIKELY(l->HasPreAlloc())) {
-      StackHandleScope<1> hs(self);
-      HandleWrapperObjPtr<mirror::Class> h_klass(hs.NewHandleWrapper(klass));
-      l->PreObjectAllocated(self, h_klass, &alloc_size);
-    }
-  }
-  *old_no_thread_suspend_cause =
-      self->StartAssertNoThreadSuspension("Called PreObjectAllocated, no suspend until alloc");
-};
-#define PERFORM_SUSPENDING_OPERATION(op)                                          \
-  [&]() REQUIRES(Roles::uninterruptible_) REQUIRES_SHARED(Locks::mutator_lock_) { \
-    release_no_suspend();                                                         \
-    auto res = (op);                                                              \
-    send_object_pre_alloc();                                                      \
-    return res;                                                                   \
-  }()
-
   StackHandleScope<1> hs(self);
   HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(klass));
   // The allocation failed. If the GC is running, block until it completes, and then retry the
@@ -1762,8 +1735,6 @@
   collector::GcType last_gc = WaitForGcToComplete(kGcCauseForAlloc, self);
   // If we were the default allocator but the allocator changed while we were suspended,
   // abort the allocation.
-  // We just waited, call the pre-alloc again.
-  send_object_pre_alloc();
   if ((was_default_allocator && allocator != GetCurrentAllocator()) ||
       (!instrumented && EntrypointsInstrumented())) {
     return nullptr;
@@ -1778,9 +1749,8 @@
   }
 
   collector::GcType tried_type = next_gc_type_;
-  const bool gc_ran = PERFORM_SUSPENDING_OPERATION(
-      CollectGarbageInternal(tried_type, kGcCauseForAlloc, false) != collector::kGcTypeNone);
-
+  const bool gc_ran =
+      CollectGarbageInternal(tried_type, kGcCauseForAlloc, false) != collector::kGcTypeNone;
   if ((was_default_allocator && allocator != GetCurrentAllocator()) ||
       (!instrumented && EntrypointsInstrumented())) {
     return nullptr;
@@ -1799,8 +1769,8 @@
       continue;
     }
     // Attempt to run the collector, if we succeed, re-try the allocation.
-    const bool plan_gc_ran = PERFORM_SUSPENDING_OPERATION(
-        CollectGarbageInternal(gc_type, kGcCauseForAlloc, false) != collector::kGcTypeNone);
+    const bool plan_gc_ran =
+        CollectGarbageInternal(gc_type, kGcCauseForAlloc, false) != collector::kGcTypeNone;
     if ((was_default_allocator && allocator != GetCurrentAllocator()) ||
         (!instrumented && EntrypointsInstrumented())) {
       return nullptr;
@@ -1830,7 +1800,7 @@
   // TODO: Run finalization, but this may cause more allocations to occur.
   // We don't need a WaitForGcToComplete here either.
   DCHECK(!gc_plan_.empty());
-  PERFORM_SUSPENDING_OPERATION(CollectGarbageInternal(gc_plan_.back(), kGcCauseForAlloc, true));
+  CollectGarbageInternal(gc_plan_.back(), kGcCauseForAlloc, true);
   if ((was_default_allocator && allocator != GetCurrentAllocator()) ||
       (!instrumented && EntrypointsInstrumented())) {
     return nullptr;
@@ -1847,8 +1817,7 @@
             current_time - last_time_homogeneous_space_compaction_by_oom_ >
             min_interval_homogeneous_space_compaction_by_oom_) {
           last_time_homogeneous_space_compaction_by_oom_ = current_time;
-          HomogeneousSpaceCompactResult result =
-              PERFORM_SUSPENDING_OPERATION(PerformHomogeneousSpaceCompact());
+          HomogeneousSpaceCompactResult result = PerformHomogeneousSpaceCompact();
           // Thread suspension could have occurred.
           if ((was_default_allocator && allocator != GetCurrentAllocator()) ||
               (!instrumented && EntrypointsInstrumented())) {
@@ -1893,13 +1862,9 @@
       }
     }
   }
-#undef PERFORM_SUSPENDING_OPERATION
   // If the allocation hasn't succeeded by this point, throw an OOM error.
   if (ptr == nullptr) {
-    release_no_suspend();
     ThrowOutOfMemoryError(self, alloc_size, allocator);
-    *old_no_thread_suspend_cause =
-        self->StartAssertNoThreadSuspension("Failed allocation fallback");
   }
   return ptr;
 }
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 6f6cfd1..9ef6af5 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -1011,10 +1011,8 @@
                                          size_t* bytes_allocated,
                                          size_t* usable_size,
                                          size_t* bytes_tl_bulk_allocated,
-                                         ObjPtr<mirror::Class>* klass,
-                                         /*out*/const char** old_no_thread_suspend_cause)
+                                         ObjPtr<mirror::Class>* klass)
       REQUIRES(!Locks::thread_suspend_count_lock_, !*gc_complete_lock_, !*pending_task_lock_)
-      ACQUIRE(Roles::uninterruptible_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Allocate into a specific space.
diff --git a/runtime/offsets.h b/runtime/offsets.h
index 2f36fe6..6d1a8e0 100644
--- a/runtime/offsets.h
+++ b/runtime/offsets.h
@@ -37,9 +37,6 @@
   constexpr size_t SizeValue() const {
     return val_;
   }
-  constexpr bool operator==(Offset o) const {
-    return SizeValue() == o.SizeValue();
-  }
 
  protected:
   size_t val_;
diff --git a/test/1983-structural-redefinition-failures/expected.txt b/test/1983-structural-redefinition-failures/expected.txt
index 40a0914..54e1bcc 100644
--- a/test/1983-structural-redefinition-failures/expected.txt
+++ b/test/1983-structural-redefinition-failures/expected.txt
@@ -28,7 +28,7 @@
 Is Structurally modifiable class java.util.Objects true
 Is Structurally modifiable class java.util.Arrays true
 Is Structurally modifiable class [Ljava.lang.Object; false
-Is Structurally modifiable class java.lang.Integer true
+Is Structurally modifiable class java.lang.Integer false
 Is Structurally modifiable class java.lang.Number false
 Is Structurally modifiable class art.Test1983$NoVirtuals true
 Is Structurally modifiable class art.Test1983$WithVirtuals false
diff --git a/test/1994-final-virtual-structural/expected.txt b/test/1994-final-virtual-structural/expected.txt
deleted file mode 100644
index 9b74d30..0000000
--- a/test/1994-final-virtual-structural/expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Hi!
-Hello world!
-Hej Verden!
-Bonjour le monde!
-こんにちは世界!
diff --git a/test/1994-final-virtual-structural/info.txt b/test/1994-final-virtual-structural/info.txt
deleted file mode 100644
index 606c984..0000000
--- a/test/1994-final-virtual-structural/info.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests basic functions in the jvmti plugin.
-
-Tests that using the structural redefinition can add new virtual methods and fields.
diff --git a/test/1994-final-virtual-structural/run b/test/1994-final-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1994-final-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1994-final-virtual-structural/src/Main.java b/test/1994-final-virtual-structural/src/Main.java
deleted file mode 100644
index 3f0cb14..0000000
--- a/test/1994-final-virtual-structural/src/Main.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public class Main {
-  public static void main(String[] args) throws Exception {
-    art.Test1994.run();
-  }
-}
diff --git a/test/1994-final-virtual-structural/src/art/Redefinition.java b/test/1994-final-virtual-structural/src/art/Redefinition.java
deleted file mode 120000
index 81eaf31..0000000
--- a/test/1994-final-virtual-structural/src/art/Redefinition.java
+++ /dev/null
@@ -1 +0,0 @@
-../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/1994-final-virtual-structural/src/art/Test1994.java b/test/1994-final-virtual-structural/src/art/Test1994.java
deleted file mode 100644
index 9ae7772..0000000
--- a/test/1994-final-virtual-structural/src/art/Test1994.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package art;
-
-import java.util.Base64;
-public class Test1994 {
-
-  public static final class Transform {
-    public void sayHi() {
-      System.out.println("Hi!");
-    }
-  }
-
-  /**
-   * base64 encoded class/dex file for
-   * public static final class Transform {
-   *   public void sayHi() {
-   *     sayHiEnglish();
-   *     sayHiDanish();
-   *     sayHiFrance();
-   *     sayHiJapan();
-   *   }
-   *   public void sayHiEnglish() {
-   *     System.out.println("Hello world!");
-   *   }
-   *   public void sayHiDanish() {
-   *     System.out.println("Hej Verden!");
-   *   }
-   *   public void sayHiJapan() {
-   *     System.out.println("こんにちは世界!");
-   *   }
-   *   public void sayHiFrance() {
-   *     System.out.println("Bonjour le monde!");
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-    "ZGV4CjAzNQA87tn3VIDgMrF+Md2W4r58elaMPcSfk2CMBQAAcAAAAHhWNBIAAAAAAAAAAMgEAAAc" +
-    "AAAAcAAAAAkAAADgAAAAAgAAAAQBAAABAAAAHAEAAAgAAAAkAQAAAQAAAGQBAAAIBAAAhAEAAG4C" +
-    "AAB2AgAAiQIAAJYCAACkAgAAvgIAAM4CAADyAgAAEgMAACkDAAA9AwAAUQMAAGUDAAB0AwAAfwMA" +
-    "AIIDAACGAwAAkwMAAJkDAACeAwAApwMAAK4DAAC7AwAAyQMAANYDAADiAwAA6QMAAGEEAAAEAAAA" +
-    "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAA4AAAAOAAAACAAAAAAAAAAPAAAACAAAAGgCAAAH" +
-    "AAQAEgAAAAAAAAAAAAAAAAAAABQAAAAAAAAAFQAAAAAAAAAWAAAAAAAAABcAAAAAAAAAGAAAAAQA" +
-    "AQATAAAABQAAAAAAAAAAAAAAEQAAAAUAAAAAAAAADAAAALgEAACIBAAAAAAAAAEAAQABAAAASAIA" +
-    "AAQAAABwEAcAAAAOAAEAAQABAAAATAIAAA0AAABuEAMAAABuEAIAAABuEAQAAABuEAUAAAAOAAAA" +
-    "AwABAAIAAABUAgAACAAAAGIAAAAaAQIAbiAGABAADgADAAEAAgAAAFkCAAAIAAAAYgAAABoBAwBu" +
-    "IAYAEAAOAAMAAQACAAAAXgIAAAgAAABiAAAAGgEBAG4gBgAQAA4AAwABAAIAAABjAgAACAAAAGIA" +
-    "AAAaARsAbiAGABAADgADAA4ABQAOPDw8PAAOAA54AAsADngAFAAOeAARAA54AAEAAAAGAAY8aW5p" +
-    "dD4AEUJvbmpvdXIgbGUgbW9uZGUhAAtIZWogVmVyZGVuIQAMSGVsbG8gd29ybGQhABhMYXJ0L1Rl" +
-    "c3QxOTk0JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5OTQ7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5j" +
-    "bG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1By" +
-    "aW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" +
-    "L2xhbmcvU3lzdGVtOwANVGVzdDE5OTQuamF2YQAJVHJhbnNmb3JtAAFWAAJWTAALYWNjZXNzRmxh" +
-    "Z3MABG5hbWUAA291dAAHcHJpbnRsbgAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gA" +
-    "C3NheUhpRnJhbmNlAApzYXlIaUphcGFuAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6" +
-    "ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiJjZDkwMDIzOTMwZDk3M2Y1NzcxMWYxZDRmZGFh" +
-    "ZDdhM2U0NzE0NjM3IiwidmVyc2lvbiI6IjEuNy4xNC1kZXYifQAI44GT44KT44Gr44Gh44Gv5LiW" +
-    "55WMIQACAgEZGAECAwIQBBkRFw0AAAEFAIGABIQDAQGcAwEByAMBAegDAQGIBAEBqAQAAAAAAAAC" +
-    "AAAAeQQAAH8EAACsBAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAAcAAAAAIA" +
-    "AAAJAAAA4AAAAAMAAAACAAAABAEAAAQAAAABAAAAHAEAAAUAAAAIAAAAJAEAAAYAAAABAAAAZAEA" +
-    "AAEgAAAGAAAAhAEAAAMgAAAGAAAASAIAAAEQAAABAAAAaAIAAAIgAAAcAAAAbgIAAAQgAAACAAAA" +
-    "eQQAAAAgAAABAAAAiAQAAAMQAAACAAAAqAQAAAYgAAABAAAAuAQAAAAQAAABAAAAyAQAAA==");
-
-  public static void run() {
-    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
-    doTest(new Transform());
-  }
-
-  public static void doTest(Transform t) {
-    t.sayHi();
-    Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
-    t.sayHi();
-  }
-}
diff --git a/test/1995-final-virtual-structural-multithread/expected.txt b/test/1995-final-virtual-structural-multithread/expected.txt
deleted file mode 100644
index e69de29..0000000
--- a/test/1995-final-virtual-structural-multithread/expected.txt
+++ /dev/null
diff --git a/test/1995-final-virtual-structural-multithread/info.txt b/test/1995-final-virtual-structural-multithread/info.txt
deleted file mode 100644
index f9b7bdd..0000000
--- a/test/1995-final-virtual-structural-multithread/info.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Tests structural redefinition with multiple threads.
-
-Tests that using the structural redefinition while concurrently using the class being redefined
-doesn't cause any unexpected problems.
diff --git a/test/1995-final-virtual-structural-multithread/run b/test/1995-final-virtual-structural-multithread/run
deleted file mode 100755
index 421f7b0..0000000
--- a/test/1995-final-virtual-structural-multithread/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# TODO(b/144168550) This test uses access patterns that can be replaced by
-# iget-object-quick during dex2dex compilation. This breaks the test since the
-# -quick opcode encodes the exact byte offset of fields. Since this test changes
-# the offset this causes problems.
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true -Xcompiler-option --debuggable
diff --git a/test/1995-final-virtual-structural-multithread/src/Main.java b/test/1995-final-virtual-structural-multithread/src/Main.java
deleted file mode 100644
index f19358d..0000000
--- a/test/1995-final-virtual-structural-multithread/src/Main.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public class Main {
-  public static void main(String[] args) throws Exception {
-    art.Test1995.run();
-  }
-}
diff --git a/test/1995-final-virtual-structural-multithread/src/art/Redefinition.java b/test/1995-final-virtual-structural-multithread/src/art/Redefinition.java
deleted file mode 120000
index 81eaf31..0000000
--- a/test/1995-final-virtual-structural-multithread/src/art/Redefinition.java
+++ /dev/null
@@ -1 +0,0 @@
-../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/1995-final-virtual-structural-multithread/src/art/Test1995.java b/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
deleted file mode 100644
index 1ffee60..0000000
--- a/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package art;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.concurrent.CountDownLatch;
-public class Test1995 {
-  private static final int NUM_THREADS = 20;
-
-  public static final class Transform {
-    public String greetingEnglish;
-    public Transform() {
-      this.greetingEnglish = "Hello";
-    }
-    public String sayHi() {
-      return greetingEnglish + " from " + Thread.currentThread().getName();
-    }
-  }
-
-  /**
-   * base64 encoded class/dex file for
-   * public static final class Transform {
-   *   public String greetingEnglish;
-   *   public String greetingFrench;
-   *   public String greetingDanish;
-   *   public String greetingJapanese;
-   *
-   *   public Transform() {
-   *     this.greetingEnglish = "Hello World";
-   *     this.greetingFrench = "Bonjour le Monde";
-   *     this.greetingDanish = "Hej Verden";
-   *     this.greetingJapanese = "こんにちは世界";
-   *   }
-   *   public String sayHi() {
-   *     return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName();
-   *   }
-   *   public String sayHiEnglish() {
-   *     return greetingEnglish;
-   *   }
-   *   public String sayHiDanish() {
-   *     return greetingDanish;
-   *   }
-   *   public String sayHiJapanese() {
-   *     return greetingJapanese;
-   *   }
-   *   public String sayHiFrench() {
-   *     return greetingFrench;
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQCsHrUqkb8cYgT2oYN7HlVbeOxJT/kONRvgBgAAcAAAAHhWNBIAAAAAAAAAABwGAAAl" +
-"AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAADoBAAA+AEAAEoD" +
-"AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" +
-"AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" +
-"7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAK8FAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" +
-"AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" +
-"AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" +
-"AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" +
-"BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAADAYAANUFAAAAAAAABwABAAIAAAAt" +
-"AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" +
-"BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" +
-"AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" +
-"NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" +
-"EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" +
-"WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" +
-"PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" +
-"GExhcnQvVGVzdDE5OTUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5NTsAIkxkYWx2aWsvYW5ub3Rh" +
-"dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
-"dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
-"bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDE5OTUuamF2YQAJVHJhbnNmb3JtAAFWAAth" +
-"Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" +
-"aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" +
-"ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" +
-"cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIs" +
-"Im1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2ZDg4" +
-"YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AB+OBk+OCk+OBq+OBoeOBr+S4lueVjAACAgEi" +
-"GAECAwITBBkbFxEABAEFAAEBAQEBAQEAgYAE7AUBAfgDAQGMBQEBpAUBAbwFAQHUBQAAAAAAAgAA" +
-"AMYFAADMBQAAAAYAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJQAAAHAAAAACAAAA" +
-"CQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAFAAAADAAAAHgBAAAGAAAAAQAAANgBAAAB" +
-"IAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQDAAACIAAAJQAAAEoDAAAEIAAAAgAAAMYF" +
-"AAAAIAAAAQAAANUFAAADEAAAAgAAAPwFAAAGIAAAAQAAAAwGAAAAEAAAAQAAABwGAAA=");
-
-
-  public static void run() throws Exception {
-    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
-    doTest();
-  }
-
-  public static final class MyThread extends Thread {
-    public MyThread(CountDownLatch delay, int id) {
-      super("Thread: " + id);
-      this.thr_id = id;
-      this.results = new ArrayList<>(1000);
-      this.finish = false;
-      this.delay = delay;
-    }
-
-    public void run() {
-      delay.countDown();
-      while (!finish) {
-        Transform t = new Transform();
-        results.add(t.sayHi());
-      }
-    }
-
-    public void finish() throws Exception {
-      finish = true;
-      this.join();
-    }
-
-    public void Check() throws Exception {
-      for (String s : results) {
-        if (!s.equals("Hello from " + getName()) &&
-            !s.equals("Hello, null, null, null from " + getName()) &&
-            !s.equals("Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " + getName())) {
-          System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
-        }
-      }
-    }
-
-    public ArrayList<String> results;
-    public volatile boolean finish;
-    public int thr_id;
-    public CountDownLatch delay;
-  }
-
-  public static MyThread[] startThreads(int num_threads) throws Exception {
-    CountDownLatch cdl = new CountDownLatch(num_threads);
-    MyThread[] res = new MyThread[num_threads];
-    for (int i = 0; i < num_threads; i++) {
-      res[i] = new MyThread(cdl, i);
-      res[i].start();
-    }
-    cdl.await();
-    return res;
-  }
-  public static void finishThreads(MyThread[] thrs) throws Exception {
-    for (MyThread t : thrs) {
-      t.finish();
-    }
-    for (MyThread t : thrs) {
-      t.Check();
-    }
-  }
-
-  public static void doTest() throws Exception {
-    MyThread[] threads = startThreads(NUM_THREADS);
-    Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
-    finishThreads(threads);
-  }
-}
diff --git a/test/1996-final-override-virtual-structural/expected.txt b/test/1996-final-override-virtual-structural/expected.txt
deleted file mode 100644
index 20cd98f..0000000
--- a/test/1996-final-override-virtual-structural/expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Not doing anything
-super: Hi this: Hi
-Redefining calling class
-super: Hi this: SALUTATIONS
-Not doing anything
-super: Hi and then this: SALUTATIONS
diff --git a/test/1996-final-override-virtual-structural/info.txt b/test/1996-final-override-virtual-structural/info.txt
deleted file mode 100644
index 55adf7c..0000000
--- a/test/1996-final-override-virtual-structural/info.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Tests basic functions in the jvmti plugin.
-
-Tests that using the structural redefinition allows one to override a superclass method.
diff --git a/test/1996-final-override-virtual-structural/run b/test/1996-final-override-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1996-final-override-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1996-final-override-virtual-structural/src/Main.java b/test/1996-final-override-virtual-structural/src/Main.java
deleted file mode 100644
index ade69cf..0000000
--- a/test/1996-final-override-virtual-structural/src/Main.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public class Main {
-  public static void main(String[] args) throws Exception {
-    art.Test1996.run();
-  }
-}
diff --git a/test/1996-final-override-virtual-structural/src/art/Redefinition.java b/test/1996-final-override-virtual-structural/src/art/Redefinition.java
deleted file mode 120000
index 81eaf31..0000000
--- a/test/1996-final-override-virtual-structural/src/art/Redefinition.java
+++ /dev/null
@@ -1 +0,0 @@
-../../../jvmti-common/Redefinition.java
\ No newline at end of file
diff --git a/test/1996-final-override-virtual-structural/src/art/Test1996.java b/test/1996-final-override-virtual-structural/src/art/Test1996.java
deleted file mode 100644
index c2b1125..0000000
--- a/test/1996-final-override-virtual-structural/src/art/Test1996.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package art;
-
-import java.util.Base64;
-public class Test1996 {
-
-  public static class SuperTransform {
-    public String hiValue = "Hi";
-    public String sayHi() {
-      return this.hiValue;
-    }
-  }
-  public static final class Transform extends SuperTransform {
-    public void PostTransform() { }
-    public String sayHiTwice(Runnable run) {
-      run.run();
-      return "super: " + super.sayHi() + " this: " + sayHi();
-    }
-  }
-
-  /**
-   * base64 encoded class/dex file for
-   * public static final class Transform extends SuperTransform {
-   *   public String myGreeting;
-   *   public void PostTransform() {
-   *     myGreeting = "SALUTATIONS";
-   *   }
-   *   public String sayHiTwice(Runnable run) {
-   *     run.run();
-   *     return "super: " + super.sayHi() + " and then this: " + sayHi();
-   *   }
-   *   public String sayHi() {
-   *     return myGreeting;
-   *   }
-   * }
-   */
-  private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQAO4Dwurw97RcUtfH7np7S5RR8gsJYOfmeABQAAcAAAAHhWNBIAAAAAAAAAALwEAAAc" +
-"AAAAcAAAAAkAAADgAAAABAAAAAQBAAABAAAANAEAAAoAAAA8AQAAAQAAAIwBAADUAwAArAEAAHYC" +
-"AACIAgAAkAIAAJMCAACXAgAAtgIAANACAADgAgAABAMAACQDAAA6AwAATgMAAGkDAAB4AwAAhQMA" +
-"AJQDAACfAwAAogMAAK8DAAC3AwAAwwMAAMkDAADOAwAA1QMAAOEDAADqAwAA9AMAAPsDAAAEAAAA" +
-"BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAABAAAAACAAAABgAAAAAAAAADAAAABgAAAGgCAAAD" +
-"AAAABwAAAHACAAAQAAAACAAAAAAAAAABAAYAEwAAAAAAAwABAAAAAAAAABYAAAABAAMAAQAAAAEA" +
-"AwAMAAAAAQAAABYAAAABAAEAFwAAAAUAAwAVAAAABwADAAEAAAAHAAIAEgAAAAcAAAAZAAAAAQAA" +
-"ABEAAAAAAAAAAAAAAA4AAACsBAAAggQAAAAAAAACAAEAAAAAAFsCAAADAAAAVBAAABEAAAAFAAIA" +
-"AgAAAF8CAAAlAAAAchAGAAQAbxABAAMADARuEAQAAwAMACIBBwBwEAcAAQAaAhgAbiAIACEAbiAI" +
-"AEEAGgQAAG4gCABBAG4gCAABAG4QCQABAAwEEQQAAAEAAQABAAAAUgIAAAQAAABwEAAAAAAOAAIA" +
-"AQAAAAAAVgIAAAUAAAAaAA0AWxAAAA4ACgAOAA0ADksAFAAOABABAA48AAAAAAEAAAAFAAAAAQAA" +
-"AAYAECBhbmQgdGhlbiB0aGlzOiAABjxpbml0PgABTAACTEwAHUxhcnQvVGVzdDE5OTYkU3VwZXJU" +
-"cmFuc2Zvcm07ABhMYXJ0L1Rlc3QxOTk2JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5OTY7ACJMZGFs" +
-"dmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
-"bGFzczsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xh" +
-"bmcvU3RyaW5nQnVpbGRlcjsADVBvc3RUcmFuc2Zvcm0AC1NBTFVUQVRJT05TAA1UZXN0MTk5Ni5q" +
-"YXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQACm15R3JlZXRpbmcABG5hbWUA" +
-"A3J1bgAFc2F5SGkACnNheUhpVHdpY2UAB3N1cGVyOiAACHRvU3RyaW5nAAV2YWx1ZQB2fn5EOHsi" +
-"Y29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2N2Iz" +
-"ODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYifQAC" +
-"AwEaGAICBAIRBBkUFw8AAQEDAAECgYAEoAQDAbgEAQGsAwEBxAMAAAAAAAACAAAAcwQAAHkEAACg" +
-"BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAJAAAA4AAAAAMA" +
-"AAAEAAAABAEAAAQAAAABAAAANAEAAAUAAAAKAAAAPAEAAAYAAAABAAAAjAEAAAEgAAAEAAAArAEA" +
-"AAMgAAAEAAAAUgIAAAEQAAACAAAAaAIAAAIgAAAcAAAAdgIAAAQgAAACAAAAcwQAAAAgAAABAAAA" +
-"ggQAAAMQAAACAAAAnAQAAAYgAAABAAAArAQAAAAQAAABAAAAvAQAAA==");
-
-  public static void run() {
-    Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
-    doTest(new Transform());
-  }
-
-  public static void doTest(final Transform t) {
-    System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
-    System.out.println(t.sayHiTwice(
-      () -> {
-        System.out.println("Redefining calling class");
-        Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
-        t.PostTransform();
-      }));
-    System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
-  }
-}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 069cecb..cd66472 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1146,9 +1146,6 @@
                   "1991-hello-structural-retransform",
                   "1992-retransform-no-such-field",
                   "1993-fallback-non-structural",
-                  "1994-final-virtual-structural",
-                  "1995-final-virtual-structural-multithread",
-                  "1996-final-override-virtual-structural",
                   "1997-structural-shadow-method",
                   "1998-structural-shadow-field"
                 ],