Merge "Update HInstruction::NeedsCurrentMethod."
diff --git a/build/art.go b/build/art.go
index b826538..1164cbc 100644
--- a/build/art.go
+++ b/build/art.go
@@ -15,7 +15,6 @@
 package art
 
 import (
-	"android/soong"
 	"android/soong/android"
 	"android/soong/cc"
 	"fmt"
@@ -222,13 +221,13 @@
 var artTestMutex sync.Mutex
 
 func init() {
-	soong.RegisterModuleType("art_cc_library", artLibrary)
-	soong.RegisterModuleType("art_cc_binary", artBinary)
-	soong.RegisterModuleType("art_cc_test", artTest)
-	soong.RegisterModuleType("art_cc_test_library", artTestLibrary)
-	soong.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
-	soong.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
-	soong.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
+	android.RegisterModuleType("art_cc_library", artLibrary)
+	android.RegisterModuleType("art_cc_binary", artBinary)
+	android.RegisterModuleType("art_cc_test", artTest)
+	android.RegisterModuleType("art_cc_test_library", artTestLibrary)
+	android.RegisterModuleType("art_cc_defaults", artDefaultsFactory)
+	android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
+	android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
 }
 
 func artGlobalDefaultsFactory() (blueprint.Module, []interface{}) {
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 0884a2a..d807fca 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -31,10 +31,6 @@
 
 namespace art {
 
-inline mirror::DexCache* CompilerDriver::GetDexCache(const DexCompilationUnit* mUnit) {
-  return mUnit->GetClassLinker()->FindDexCache(Thread::Current(), *mUnit->GetDexFile(), false);
-}
-
 inline mirror::ClassLoader* CompilerDriver::GetClassLoader(const ScopedObjectAccess& soa,
                                                            const DexCompilationUnit* mUnit) {
   return soa.Decode<mirror::ClassLoader>(mUnit->GetClassLoader()).Ptr();
@@ -87,10 +83,6 @@
   return resolved_field;
 }
 
-inline mirror::DexCache* CompilerDriver::FindDexCache(const DexFile* dex_file) {
-  return Runtime::Current()->GetClassLinker()->FindDexCache(Thread::Current(), *dex_file, false);
-}
-
 inline ArtField* CompilerDriver::ResolveField(
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
@@ -100,23 +92,6 @@
                                  is_static);
 }
 
-inline void CompilerDriver::GetResolvedFieldDexFileLocation(
-    ArtField* resolved_field, const DexFile** declaring_dex_file,
-    uint16_t* declaring_class_idx, uint16_t* declaring_field_idx) {
-  ObjPtr<mirror::Class> declaring_class = resolved_field->GetDeclaringClass();
-  *declaring_dex_file = declaring_class->GetDexCache()->GetDexFile();
-  *declaring_class_idx = declaring_class->GetDexTypeIndex();
-  *declaring_field_idx = resolved_field->GetDexFieldIndex();
-}
-
-inline bool CompilerDriver::IsFieldVolatile(ArtField* field) {
-  return field->IsVolatile();
-}
-
-inline MemberOffset CompilerDriver::GetFieldOffset(ArtField* field) {
-  return field->GetOffset();
-}
-
 inline std::pair<bool, bool> CompilerDriver::IsFastInstanceField(
     mirror::DexCache* dex_cache, mirror::Class* referrer_class,
     ArtField* resolved_field, uint16_t field_idx) {
@@ -219,43 +194,6 @@
   return result.first;
 }
 
-inline bool CompilerDriver::IsStaticFieldInReferrerClass(mirror::Class* referrer_class,
-                                                         ArtField* resolved_field) {
-  DCHECK(resolved_field->IsStatic());
-  ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
-  return referrer_class == fields_class;
-}
-
-inline bool CompilerDriver::CanAssumeClassIsInitialized(mirror::Class* klass) {
-  // Being loaded is a pre-requisite for being initialized but let's do the cheap check first.
-  //
-  // NOTE: When AOT compiling an app, we eagerly initialize app classes (and potentially their
-  // super classes in the boot image) but only those that have a trivial initialization, i.e.
-  // without <clinit>() or static values in the dex file for that class or any of its super
-  // classes. So while we could see the klass as initialized during AOT compilation and have
-  // it only loaded at runtime, the needed initialization would have to be trivial and
-  // unobservable from Java, so we may as well treat it as initialized.
-  if (!klass->IsInitialized()) {
-    return false;
-  }
-  return CanAssumeClassIsLoaded(klass);
-}
-
-inline bool CompilerDriver::CanReferrerAssumeClassIsInitialized(mirror::Class* referrer_class,
-                                                                mirror::Class* klass) {
-  return (referrer_class != nullptr
-          && !referrer_class->IsInterface()
-          && referrer_class->IsSubClass(klass))
-      || CanAssumeClassIsInitialized(klass);
-}
-
-inline bool CompilerDriver::IsStaticFieldsClassInitialized(mirror::Class* referrer_class,
-                                                           ArtField* resolved_field) {
-  DCHECK(resolved_field->IsStatic());
-  ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
-  return CanReferrerAssumeClassIsInitialized(referrer_class, fields_class.Ptr());
-}
-
 inline ArtMethod* CompilerDriver::ResolveMethod(
     ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit,
@@ -275,35 +213,6 @@
   return resolved_method;
 }
 
-inline void CompilerDriver::GetResolvedMethodDexFileLocation(
-    ArtMethod* resolved_method, const DexFile** declaring_dex_file,
-    uint16_t* declaring_class_idx, uint16_t* declaring_method_idx) {
-  mirror::Class* declaring_class = resolved_method->GetDeclaringClass();
-  *declaring_dex_file = declaring_class->GetDexCache()->GetDexFile();
-  *declaring_class_idx = declaring_class->GetDexTypeIndex();
-  *declaring_method_idx = resolved_method->GetDexMethodIndex();
-}
-
-inline uint16_t CompilerDriver::GetResolvedMethodVTableIndex(
-    ArtMethod* resolved_method, InvokeType type) {
-  if (type == kVirtual || type == kSuper) {
-    return resolved_method->GetMethodIndex();
-  } else if (type == kInterface) {
-    return resolved_method->GetDexMethodIndex();
-  } else {
-    return DexFile::kDexNoIndex16;
-  }
-}
-
-inline bool CompilerDriver::IsMethodsClassInitialized(mirror::Class* referrer_class,
-                                                      ArtMethod* resolved_method) {
-  if (!resolved_method->IsStatic()) {
-    return true;
-  }
-  mirror::Class* methods_class = resolved_method->GetDeclaringClass();
-  return CanReferrerAssumeClassIsInitialized(referrer_class, methods_class);
-}
-
 }  // namespace art
 
 #endif  // ART_COMPILER_DRIVER_COMPILER_DRIVER_INL_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 414a43c..8d64c65 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -95,8 +95,6 @@
  public:
   AOTCompilationStats()
       : stats_lock_("AOT compilation statistics lock"),
-        types_in_dex_cache_(0), types_not_in_dex_cache_(0),
-        strings_in_dex_cache_(0), strings_not_in_dex_cache_(0),
         resolved_types_(0), unresolved_types_(0),
         resolved_instance_fields_(0), unresolved_instance_fields_(0),
         resolved_local_static_fields_(0), resolved_static_fields_(0), unresolved_static_fields_(0),
@@ -112,8 +110,6 @@
   }
 
   void Dump() {
-    DumpStat(types_in_dex_cache_, types_not_in_dex_cache_, "types known to be in dex cache");
-    DumpStat(strings_in_dex_cache_, strings_not_in_dex_cache_, "strings known to be in dex cache");
     DumpStat(resolved_types_, unresolved_types_, "types resolved");
     DumpStat(resolved_instance_fields_, unresolved_instance_fields_, "instance fields resolved");
     DumpStat(resolved_local_static_fields_ + resolved_static_fields_, unresolved_static_fields_,
@@ -164,26 +160,6 @@
 #define STATS_LOCK()
 #endif
 
-  void TypeInDexCache() REQUIRES(!stats_lock_) {
-    STATS_LOCK();
-    types_in_dex_cache_++;
-  }
-
-  void TypeNotInDexCache() REQUIRES(!stats_lock_) {
-    STATS_LOCK();
-    types_not_in_dex_cache_++;
-  }
-
-  void StringInDexCache() REQUIRES(!stats_lock_) {
-    STATS_LOCK();
-    strings_in_dex_cache_++;
-  }
-
-  void StringNotInDexCache() REQUIRES(!stats_lock_) {
-    STATS_LOCK();
-    strings_not_in_dex_cache_++;
-  }
-
   void TypeDoesntNeedAccessCheck() REQUIRES(!stats_lock_) {
     STATS_LOCK();
     resolved_types_++;
@@ -225,67 +201,6 @@
     type_based_devirtualization_++;
   }
 
-  // Indicate that a method of the given type was resolved at compile time.
-  void ResolvedMethod(InvokeType type) REQUIRES(!stats_lock_) {
-    DCHECK_LE(type, kMaxInvokeType);
-    STATS_LOCK();
-    resolved_methods_[type]++;
-  }
-
-  // Indicate that a method of the given type was unresolved at compile time as it was in an
-  // unknown dex file.
-  void UnresolvedMethod(InvokeType type) REQUIRES(!stats_lock_) {
-    DCHECK_LE(type, kMaxInvokeType);
-    STATS_LOCK();
-    unresolved_methods_[type]++;
-  }
-
-  // Indicate that a type of virtual method dispatch has been converted into a direct method
-  // dispatch.
-  void VirtualMadeDirect(InvokeType type) REQUIRES(!stats_lock_) {
-    DCHECK(type == kVirtual || type == kInterface || type == kSuper);
-    STATS_LOCK();
-    virtual_made_direct_[type]++;
-  }
-
-  // Indicate that a method of the given type was able to call directly into boot.
-  void DirectCallsToBoot(InvokeType type) REQUIRES(!stats_lock_) {
-    DCHECK_LE(type, kMaxInvokeType);
-    STATS_LOCK();
-    direct_calls_to_boot_[type]++;
-  }
-
-  // Indicate that a method of the given type was able to be resolved directly from boot.
-  void DirectMethodsToBoot(InvokeType type) REQUIRES(!stats_lock_) {
-    DCHECK_LE(type, kMaxInvokeType);
-    STATS_LOCK();
-    direct_methods_to_boot_[type]++;
-  }
-
-  void ProcessedInvoke(InvokeType type, int flags) REQUIRES(!stats_lock_) {
-    STATS_LOCK();
-    if (flags == 0) {
-      unresolved_methods_[type]++;
-    } else {
-      DCHECK_NE((flags & kFlagMethodResolved), 0);
-      resolved_methods_[type]++;
-      if ((flags & kFlagVirtualMadeDirect) != 0) {
-        virtual_made_direct_[type]++;
-        if ((flags & kFlagPreciseTypeDevirtualization) != 0) {
-          type_based_devirtualization_++;
-        }
-      } else {
-        DCHECK_EQ((flags & kFlagPreciseTypeDevirtualization), 0);
-      }
-      if ((flags & kFlagDirectCallToBoot) != 0) {
-        direct_calls_to_boot_[type]++;
-      }
-      if ((flags & kFlagDirectMethodToBoot) != 0) {
-        direct_methods_to_boot_[type]++;
-      }
-    }
-  }
-
   // A check-cast could be eliminated due to verifier type analysis.
   void SafeCast() REQUIRES(!stats_lock_) {
     STATS_LOCK();
@@ -301,12 +216,6 @@
  private:
   Mutex stats_lock_;
 
-  size_t types_in_dex_cache_;
-  size_t types_not_in_dex_cache_;
-
-  size_t strings_in_dex_cache_;
-  size_t strings_not_in_dex_cache_;
-
   size_t resolved_types_;
   size_t unresolved_types_;
 
@@ -848,9 +757,10 @@
 // TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
 //       stable order.
 
-static void ResolveConstStrings(CompilerDriver* driver,
+static void ResolveConstStrings(Handle<mirror::DexCache> dex_cache,
                                 const DexFile& dex_file,
-                                const DexFile::CodeItem* code_item) {
+                                const DexFile::CodeItem* code_item)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
   if (code_item == nullptr) {
     // Abstract or native method.
     return;
@@ -858,18 +768,18 @@
 
   const uint16_t* code_ptr = code_item->insns_;
   const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
 
   while (code_ptr < code_end) {
     const Instruction* inst = Instruction::At(code_ptr);
     switch (inst->Opcode()) {
-      case Instruction::CONST_STRING: {
-        uint32_t string_index = inst->VRegB_21c();
-        driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
-        break;
-      }
+      case Instruction::CONST_STRING:
       case Instruction::CONST_STRING_JUMBO: {
-        uint32_t string_index = inst->VRegB_31c();
-        driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+        uint32_t string_index = (inst->Opcode() == Instruction::CONST_STRING)
+            ? inst->VRegB_21c()
+            : inst->VRegB_31c();
+        mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache);
+        CHECK(string != nullptr) << "Could not allocate a string when forcing determinism";
         break;
       }
 
@@ -884,7 +794,13 @@
 static void ResolveConstStrings(CompilerDriver* driver,
                                 const std::vector<const DexFile*>& dex_files,
                                 TimingLogger* timings) {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  MutableHandle<mirror::DexCache> dex_cache(hs.NewHandle<mirror::DexCache>(nullptr));
+
   for (const DexFile* dex_file : dex_files) {
+    dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file, false));
     TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
 
     size_t class_def_count = dex_file->NumClassDefs();
@@ -925,7 +841,7 @@
           continue;
         }
         previous_direct_method_idx = method_idx;
-        ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+        ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
         it.Next();
       }
       // Virtual methods.
@@ -939,7 +855,7 @@
           continue;
         }
         previous_virtual_method_idx = method_idx;
-        ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+        ResolveConstStrings(dex_cache, *dex_file, it.GetMethodCodeItem());
         it.Next();
       }
       DCHECK(!it.HasNext());
@@ -1410,54 +1326,6 @@
   dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index);
 }
 
-bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache,
-                                                      uint32_t type_idx) {
-  bool result = false;
-  if ((GetCompilerOptions().IsBootImage() &&
-       IsImageClass(dex_cache->GetDexFile()->StringDataByIdx(
-           dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) ||
-      Runtime::Current()->UseJitCompilation()) {
-    mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
-    result = (resolved_class != nullptr);
-  }
-
-  if (result) {
-    stats_->TypeInDexCache();
-  } else {
-    stats_->TypeNotInDexCache();
-  }
-  return result;
-}
-
-bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file,
-                                                        uint32_t string_idx) {
-  // See also Compiler::ResolveDexFile
-
-  bool result = false;
-  if (GetCompilerOptions().IsBootImage() || Runtime::Current()->UseJitCompilation()) {
-    ScopedObjectAccess soa(Thread::Current());
-    StackHandleScope<1> hs(soa.Self());
-    ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-    Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(
-        soa.Self(), dex_file, false)));
-    if (GetCompilerOptions().IsBootImage()) {
-      // We resolve all const-string strings when building for the image.
-      class_linker->ResolveString(dex_file, string_idx, dex_cache);
-      result = true;
-    } else {
-      // Just check whether the dex cache already has the string.
-      DCHECK(Runtime::Current()->UseJitCompilation());
-      result = (dex_cache->GetResolvedString(string_idx) != nullptr);
-    }
-  }
-  if (result) {
-    stats_->StringInDexCache();
-  } else {
-    stats_->StringNotInDexCache();
-  }
-  return result;
-}
-
 bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx,
                                                 Handle<mirror::DexCache> dex_cache,
                                                 uint32_t type_idx) {
@@ -1521,108 +1389,6 @@
   return result;
 }
 
-bool CompilerDriver::CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx,
-                                        bool* is_type_initialized, bool* use_direct_type_ptr,
-                                        uintptr_t* direct_type_ptr, bool* out_is_finalizable) {
-  ScopedObjectAccess soa(Thread::Current());
-  Runtime* runtime = Runtime::Current();
-  mirror::DexCache* dex_cache = runtime->GetClassLinker()->FindDexCache(
-      soa.Self(), dex_file, false);
-  mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx);
-  if (resolved_class == nullptr) {
-    return false;
-  }
-  if (GetCompilerOptions().GetCompilePic()) {
-    // Do not allow a direct class pointer to be used when compiling for position-independent
-    return false;
-  }
-  *out_is_finalizable = resolved_class->IsFinalizable();
-  gc::Heap* heap = runtime->GetHeap();
-  const bool compiling_boot = heap->IsCompilingBoot();
-  const bool support_boot_image_fixup = GetSupportBootImageFixup();
-  if (compiling_boot) {
-    // boot -> boot class pointers.
-    // True if the class is in the image at boot compiling time.
-    const bool is_image_class = GetCompilerOptions().IsBootImage() && IsImageClass(
-        dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_));
-    // True if pc relative load works.
-    if (is_image_class && support_boot_image_fixup) {
-      *is_type_initialized = resolved_class->IsInitialized();
-      *use_direct_type_ptr = false;
-      *direct_type_ptr = 0;
-      return true;
-    } else {
-      return false;
-    }
-  } else if (runtime->UseJitCompilation() && !heap->IsMovableObject(resolved_class)) {
-    *is_type_initialized = resolved_class->IsInitialized();
-    // If the class may move around, then don't embed it as a direct pointer.
-    *use_direct_type_ptr = true;
-    *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class);
-    return true;
-  } else {
-    // True if the class is in the image at app compiling time.
-    const bool class_in_image = heap->FindSpaceFromObject(resolved_class, false)->IsImageSpace();
-    if (class_in_image && support_boot_image_fixup) {
-      // boot -> app class pointers.
-      *is_type_initialized = resolved_class->IsInitialized();
-      // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
-      *use_direct_type_ptr = !GetCompilerOptions().GetIncludePatchInformation();
-      *direct_type_ptr = reinterpret_cast<uintptr_t>(resolved_class);
-      return true;
-    } else {
-      // app -> app class pointers.
-      // Give up because app does not have an image and class
-      // isn't created at compile time.  TODO: implement this
-      // if/when each app gets an image.
-      return false;
-    }
-  }
-}
-
-bool CompilerDriver::CanEmbedReferenceTypeInCode(ClassReference* ref,
-                                                 bool* use_direct_ptr,
-                                                 uintptr_t* direct_type_ptr) {
-  CHECK(ref != nullptr);
-  CHECK(use_direct_ptr != nullptr);
-  CHECK(direct_type_ptr != nullptr);
-
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* reference_class = mirror::Reference::GetJavaLangRefReference();
-  bool is_initialized = false;
-  bool unused_finalizable;
-  // Make sure we have a finished Reference class object before attempting to use it.
-  if (!CanEmbedTypeInCode(*reference_class->GetDexCache()->GetDexFile(),
-                          reference_class->GetDexTypeIndex(), &is_initialized,
-                          use_direct_ptr, direct_type_ptr, &unused_finalizable) ||
-      !is_initialized) {
-    return false;
-  }
-  ref->first = &reference_class->GetDexFile();
-  ref->second = reference_class->GetDexClassDefIndex();
-  return true;
-}
-
-uint32_t CompilerDriver::GetReferenceSlowFlagOffset() const {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* klass = mirror::Reference::GetJavaLangRefReference();
-  DCHECK(klass->IsInitialized());
-  return klass->GetSlowPathFlagOffset().Uint32Value();
-}
-
-uint32_t CompilerDriver::GetReferenceDisableFlagOffset() const {
-  ScopedObjectAccess soa(Thread::Current());
-  mirror::Class* klass = mirror::Reference::GetJavaLangRefReference();
-  DCHECK(klass->IsInitialized());
-  return klass->GetDisableIntrinsicFlagOffset().Uint32Value();
-}
-
-DexCacheArraysLayout CompilerDriver::GetDexCacheArraysLayout(const DexFile* dex_file) {
-  return ContainsElement(GetDexFilesForOatFile(), dex_file)
-      ? DexCacheArraysLayout(GetInstructionSetPointerSize(instruction_set_), dex_file)
-      : DexCacheArraysLayout();
-}
-
 void CompilerDriver::ProcessedInstanceField(bool resolved) {
   if (!resolved) {
     stats_->UnresolvedInstanceField();
@@ -1641,10 +1407,6 @@
   }
 }
 
-void CompilerDriver::ProcessedInvoke(InvokeType invoke_type, int flags) {
-  stats_->ProcessedInvoke(invoke_type, flags);
-}
-
 ArtField* CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx,
                                                    const DexCompilationUnit* mUnit, bool is_put,
                                                    const ScopedObjectAccess& soa) {
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index eb1222c..9a4dd85 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -182,15 +182,6 @@
                                   uint16_t class_def_index)
       REQUIRES(!requires_constructor_barrier_lock_);
 
-  // Callbacks from compiler to see what runtime checks must be generated.
-
-  bool CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache,
-                                        uint32_t type_idx)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx)
-      REQUIRES(!Locks::mutator_lock_);
-
   // Are runtime access checks necessary in the compiled code?
   bool CanAccessTypeWithoutChecks(uint32_t referrer_idx,
                                   Handle<mirror::DexCache> dex_cache,
@@ -205,24 +196,6 @@
                                               bool* out_is_finalizable)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx,
-                          bool* is_type_initialized, bool* use_direct_type_ptr,
-                          uintptr_t* direct_type_ptr, bool* out_is_finalizable);
-
-  // Query methods for the java.lang.ref.Reference class.
-  bool CanEmbedReferenceTypeInCode(ClassReference* ref,
-                                   bool* use_direct_type_ptr, uintptr_t* direct_type_ptr);
-  uint32_t GetReferenceSlowFlagOffset() const;
-  uint32_t GetReferenceDisableFlagOffset() const;
-
-  // Get the DexCache for the
-  mirror::DexCache* GetDexCache(const DexCompilationUnit* mUnit)
-    REQUIRES_SHARED(Locks::mutator_lock_);
-
-  mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa,
-                                      const DexCompilationUnit* mUnit)
-    REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Resolve compiling method's class. Returns null on failure.
   mirror::Class* ResolveCompilingMethodsClass(
       const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
@@ -250,19 +223,6 @@
       uint32_t field_idx, bool is_static)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Get declaration location of a resolved field.
-  void GetResolvedFieldDexFileLocation(
-      ArtField* resolved_field, const DexFile** declaring_dex_file,
-      uint16_t* declaring_class_idx, uint16_t* declaring_field_idx)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  bool IsFieldVolatile(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
-  MemberOffset GetFieldOffset(ArtField* field) REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Find a dex cache for a dex file.
-  inline mirror::DexCache* FindDexCache(const DexFile* dex_file)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Can we fast-path an IGET/IPUT access to an instance field? If yes, compute the field offset.
   std::pair<bool, bool> IsFastInstanceField(
       mirror::DexCache* dex_cache, mirror::Class* referrer_class,
@@ -288,15 +248,6 @@
                                                 uint32_t* storage_index)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Is static field's in referrer's class?
-  bool IsStaticFieldInReferrerClass(mirror::Class* referrer_class, ArtField* resolved_field)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Is static field's class initialized?
-  bool IsStaticFieldsClassInitialized(mirror::Class* referrer_class,
-                                      ArtField* resolved_field)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Resolve a method. Returns null on failure, including incompatible class change.
   ArtMethod* ResolveMethod(
       ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
@@ -304,37 +255,8 @@
       uint32_t method_idx, InvokeType invoke_type, bool check_incompatible_class_change = true)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Get declaration location of a resolved field.
-  void GetResolvedMethodDexFileLocation(
-      ArtMethod* resolved_method, const DexFile** declaring_dex_file,
-      uint16_t* declaring_class_idx, uint16_t* declaring_method_idx)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Get the index in the vtable of the method.
-  uint16_t GetResolvedMethodVTableIndex(
-      ArtMethod* resolved_method, InvokeType type)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Is method's class initialized for an invoke?
-  // For static invokes to determine whether we need to consider potential call to <clinit>().
-  // For non-static invokes, assuming a non-null reference, the class is always initialized.
-  bool IsMethodsClassInitialized(mirror::Class* referrer_class, ArtMethod* resolved_method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Get the layout of dex cache arrays for a dex file. Returns invalid layout if the
-  // dex cache arrays don't have a fixed layout.
-  DexCacheArraysLayout GetDexCacheArraysLayout(const DexFile* dex_file);
-
   void ProcessedInstanceField(bool resolved);
   void ProcessedStaticField(bool resolved, bool local);
-  void ProcessedInvoke(InvokeType invoke_type, int flags);
-
-  void ComputeFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
-                        const ScopedObjectAccess& soa, bool is_static,
-                        ArtField** resolved_field,
-                        mirror::Class** referrer_class,
-                        mirror::DexCache** dex_cache)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Can we fast path instance field access? Computes field's offset and volatility.
   bool ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
@@ -386,6 +308,7 @@
   void SetDedupeEnabled(bool dedupe_enabled) {
     compiled_method_storage_.SetDedupeEnabled(dedupe_enabled);
   }
+
   bool DedupeEnabled() const {
     return compiled_method_storage_.DedupeEnabled();
   }
@@ -449,6 +372,13 @@
     return current_dex_to_dex_methods_;
   }
 
+  // Compute constant code and method pointers when possible.
+  void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
+                                     ArtMethod* method,
+                                     /* out */ uintptr_t* direct_code,
+                                     /* out */ uintptr_t* direct_method)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   // Return whether the declaring class of `resolved_member` is
   // available to `referrer_class` for read or write access using two
@@ -477,38 +407,9 @@
                                       uint32_t field_idx)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Can we assume that the klass is initialized?
-  bool CanAssumeClassIsInitialized(mirror::Class* klass)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-  bool CanReferrerAssumeClassIsInitialized(mirror::Class* referrer_class, mirror::Class* klass)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics.
-  // The only external contract is that unresolved method has flags 0 and resolved non-0.
-  enum {
-    kBitMethodResolved = 0,
-    kBitVirtualMadeDirect,
-    kBitPreciseTypeDevirtualization,
-    kBitDirectCallToBoot,
-    kBitDirectMethodToBoot
-  };
-  static constexpr int kFlagMethodResolved              = 1 << kBitMethodResolved;
-  static constexpr int kFlagVirtualMadeDirect           = 1 << kBitVirtualMadeDirect;
-  static constexpr int kFlagPreciseTypeDevirtualization = 1 << kBitPreciseTypeDevirtualization;
-  static constexpr int kFlagDirectCallToBoot            = 1 << kBitDirectCallToBoot;
-  static constexpr int kFlagDirectMethodToBoot          = 1 << kBitDirectMethodToBoot;
-  static constexpr int kFlagsMethodResolvedVirtualMadeDirect =
-      kFlagMethodResolved | kFlagVirtualMadeDirect;
-  static constexpr int kFlagsMethodResolvedPreciseTypeDevirtualization =
-      kFlagsMethodResolvedVirtualMadeDirect | kFlagPreciseTypeDevirtualization;
-
- public:  // TODO make private or eliminate.
-  // Compute constant code and method pointers when possible.
-  void GetCodeAndMethodForDirectCall(const mirror::Class* referrer_class,
-                                     ArtMethod* method,
-                                     /* out */ uintptr_t* direct_code,
-                                     /* out */ uintptr_t* direct_method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
+  mirror::ClassLoader* GetClassLoader(const ScopedObjectAccess& soa,
+                                      const DexCompilationUnit* mUnit)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
  private:
   void PreCompile(jobject class_loader,
@@ -566,8 +467,6 @@
       REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
 
   void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
-  static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
 
   void Compile(jobject class_loader,
                const std::vector<const DexFile*>& dex_files,
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 8ae04a1..13c73dc 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1389,7 +1389,7 @@
 
 void ImageWriter::CalculateNewObjectOffsets() {
   Thread* const self = Thread::Current();
-  StackHandleScopeCollection handles(self);
+  VariableSizedHandleScope handles(self);
   std::vector<Handle<ObjectArray<Object>>> image_roots;
   for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
     image_roots.push_back(handles.NewHandle(CreateImageRoots(i)));
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 0cf5f18..afb8fce 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -530,7 +530,7 @@
         << "invocations have finished (as before they were invoked).";
   }
 
-  HandleScope* const handle_scope_;
+  BaseHandleScope* const handle_scope_;
 };
 
 static void expectNumStackReferences(size_t val1, size_t val2) {
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 7e58d78..bfb342f 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -322,7 +322,7 @@
     ThreadOffset<kPointerSize> jni_start =
         is_synchronized
             ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStartSynchronized)
-            : (is_fast_native
+            : ((is_fast_native && !reference_return)  // TODO: support @FastNative returning obj
                    ? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastStart)
                    : QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart));
 
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 580ef72..f896f11 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -43,7 +43,7 @@
                 OptimizingCompilerStats* compiler_stats,
                 const uint8_t* interpreter_metadata,
                 Handle<mirror::DexCache> dex_cache,
-                StackHandleScopeCollection* handles)
+                VariableSizedHandleScope* handles)
       : graph_(graph),
         dex_file_(dex_file),
         code_item_(code_item),
@@ -68,7 +68,7 @@
   // Only for unit testing.
   HGraphBuilder(HGraph* graph,
                 const DexFile::CodeItem& code_item,
-                StackHandleScopeCollection* handles,
+                VariableSizedHandleScope* handles,
                 Primitive::Type return_type = Primitive::kPrimInt)
       : graph_(graph),
         dex_file_(nullptr),
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index c501ccf..55fcb12 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -87,11 +87,12 @@
     : HOptimization(graph, kInductionPassName),
       global_depth_(0),
       stack_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
-      scc_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
       map_(std::less<HInstruction*>(),
            graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
+      scc_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
       cycle_(std::less<HInstruction*>(),
              graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
+      type_(Primitive::kPrimVoid),
       induction_(std::less<HLoopInformation*>(),
                  graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)) {
 }
@@ -103,7 +104,6 @@
   for (HReversePostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
     HBasicBlock* graph_block = it_graph.Current();
     // Don't analyze irreducible loops.
-    // TODO(ajcbik): could/should we remove this restriction?
     if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) {
       VisitLoop(graph_block->GetLoopInformation());
     }
@@ -121,7 +121,7 @@
     HBasicBlock* loop_block = it_loop.Current();
     DCHECK(loop_block->IsInLoop());
     if (loop_block->GetLoopInformation() != loop) {
-      continue;  // Inner loops already visited.
+      continue;  // Inner loops visited later.
     }
     // Visit phi-operations and instructions.
     for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
@@ -285,6 +285,9 @@
     } else if (instruction->IsSub()) {
       update = SolveAddSub(
           loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+    } else if (instruction->IsXor()) {
+      update = SolveXor(
+          loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), true);
     } else if (instruction->IsTypeConversion()) {
       update = SolveCnv(instruction->AsTypeConversion());
     }
@@ -553,6 +556,27 @@
   return nullptr;
 }
 
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveXor(HLoopInformation* loop,
+                                                                      HInstruction* entry_phi,
+                                                                      HInstruction* instruction,
+                                                                      HInstruction* x,
+                                                                      HInstruction* y,
+                                                                      bool is_first_call) {
+  InductionInfo* b = LookupInfo(loop, y);
+  // Solve within a tight cycle on x = x ^ c.
+  if (b != nullptr && b->induction_class == kInvariant) {
+    if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
+      InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+      return CreateInduction(kPeriodic, CreateInvariantOp(kXor, initial, b), initial, type_);
+    }
+  }
+  // Try the other way around if considered for first time.
+  if (is_first_call) {
+    return SolveXor(loop, entry_phi, instruction, y, x, false);
+  }
+  return nullptr;
+}
+
 HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
   Primitive::Type from = conversion->GetInputType();
   Primitive::Type to = conversion->GetResultType();
@@ -850,8 +874,8 @@
   int64_t value = -1;
   if (IsExact(a, &value)) {
     if (value == 0) {
-      // Simplify 0 + b = b, 0 * b = 0.
-      if (op == kAdd) {
+      // Simplify 0 + b = b, 0 ^ b = b, 0 * b = 0.
+      if (op == kAdd || op == kXor) {
         return b;
       } else if (op == kMul) {
         return a;
@@ -867,8 +891,8 @@
   }
   if (IsExact(b, &value)) {
     if (value == 0) {
-      // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0.
-      if (op == kAdd || op == kSub) {
+      // Simplify a + 0 = a, a - 0 = a, a ^ 0 = a, a * 0 = 0, -0 = 0.
+      if (op == kAdd || op == kSub || op == kXor) {
         return a;
       } else if (op == kMul || op == kNeg) {
         return b;
@@ -939,6 +963,7 @@
         case kNeg:   inv += " - ";  break;
         case kMul:   inv += " * ";  break;
         case kDiv:   inv += " / ";  break;
+        case kXor:   inv += " ^ ";  break;
         case kLT:    inv += " < ";  break;
         case kLE:    inv += " <= "; break;
         case kGT:    inv += " > ";  break;
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index cd4c830..06aee31 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -64,6 +64,7 @@
     kNeg,
     kMul,
     kDiv,
+    kXor,
     kFetch,
     // Trip-counts.
     kTripCountInLoop,        // valid in full loop; loop is finite
@@ -171,7 +172,13 @@
                              HInstruction* x,
                              HInstruction* y,
                              InductionOp op,
-                             bool is_first_call);
+                             bool is_first_call);  // possibly swaps x and y to try again
+  InductionInfo* SolveXor(HLoopInformation* loop,
+                          HInstruction* entry_phi,
+                          HInstruction* instruction,
+                          HInstruction* x,
+                          HInstruction* y,
+                          bool is_first_call);  // possibly swaps x and y to try again
   InductionInfo* SolveCnv(HTypeConversion* conversion);
 
   // Trip count information.
@@ -219,8 +226,8 @@
   // Temporary book-keeping during the analysis.
   uint32_t global_depth_;
   ArenaVector<HInstruction*> stack_;
-  ArenaVector<HInstruction*> scc_;
   ArenaSafeMap<HInstruction*, NodeInfo> map_;
+  ArenaVector<HInstruction*> scc_;
   ArenaSafeMap<HInstruction*, InductionInfo*> cycle_;
   Primitive::Type type_;
 
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 292bc4e..7c467f6 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -107,7 +107,7 @@
   }
 
   // Builds if-statement at depth d.
-  HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) {
+  HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) {
     HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
     HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
     HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
@@ -259,15 +259,15 @@
   //   k = - i;
   // }
   BuildLoopNest(1);
-  HInstruction *add = InsertInstruction(
+  HInstruction* add = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
-  HInstruction *mul = InsertInstruction(
+  HInstruction* mul = InsertInstruction(
       new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
-  HInstruction *shl = InsertInstruction(
+  HInstruction* shl = InsertInstruction(
       new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
-  HInstruction *neg = InsertInstruction(
+  HInstruction* neg = InsertInstruction(
       new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
   PerformInductionVarAnalysis();
 
@@ -291,10 +291,10 @@
   HPhi* k = InsertLoopPhi(0, 0);
   k->AddInput(constant0_);
 
-  HInstruction *add = InsertInstruction(
+  HInstruction* add = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
   HInstruction* store1 = InsertArrayStore(add, 0);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
   HInstruction* store2 = InsertArrayStore(sub, 0);
   k->AddInput(sub);
@@ -381,7 +381,7 @@
   k->AddInput(constant0_);
 
   HInstruction* store = InsertArrayStore(k, 0);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
   k->AddInput(sub);
   PerformInductionVarAnalysis();
@@ -407,7 +407,7 @@
 
   HInstruction* store = InsertArrayStore(k, 0);
   k->AddInput(t);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
   t->AddInput(sub);
   PerformInductionVarAnalysis();
@@ -431,15 +431,15 @@
   HPhi* k = InsertLoopPhi(0, 0);
   k->AddInput(constant0_);
 
-  HInstruction *add = InsertInstruction(
+  HInstruction* add = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
-  HInstruction *mul = InsertInstruction(
+  HInstruction* mul = InsertInstruction(
       new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
-  HInstruction *shl = InsertInstruction(
+  HInstruction* shl = InsertInstruction(
       new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
-  HInstruction *neg = InsertInstruction(
+  HInstruction* neg = InsertInstruction(
       new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
   k->AddInput(
       InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
@@ -497,7 +497,7 @@
   k->AddInput(constant0_);
 
   HInstruction* store = InsertArrayStore(k, 0);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
   k->AddInput(sub);
   PerformInductionVarAnalysis();
@@ -506,6 +506,45 @@
   EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
 }
 
+TEST_F(InductionVarAnalysisTest, FindXorPeriodicInduction) {
+  // Setup:
+  // k = 0;
+  // for (int i = 0; i < 100; i++) {
+  //   a[k] = 0;
+  //   k = k ^ 1;
+  // }
+  BuildLoopNest(1);
+  HPhi* k = InsertLoopPhi(0, 0);
+  k->AddInput(constant0_);
+
+  HInstruction* store = InsertArrayStore(k, 0);
+  HInstruction* x = InsertInstruction(
+      new (&allocator_) HXor(Primitive::kPrimInt, k, constant1_), 0);
+  k->AddInput(x);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+  EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(x, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) {
+  // Setup:
+  // k = 100;
+  // for (int i = 0; i < 100; i++) {
+  //   k = k ^ 100;
+  // }
+  BuildLoopNest(1);
+  HPhi* k = InsertLoopPhi(0, 0);
+  k->AddInput(constant100_);
+
+  HInstruction* x = InsertInstruction(
+      new (&allocator_) HXor(Primitive::kPrimInt, k, constant100_), 0);
+  k->AddInput(x);
+  PerformInductionVarAnalysis();
+
+  EXPECT_STREQ("periodic(((100) ^ (100)), (100)):PrimInt", GetInductionInfo(x, 0).c_str());
+}
+
 TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
   // Setup:
   // k = 0;
@@ -526,15 +565,15 @@
   k_header->AddInput(k_body);
 
   // Derived expressions.
-  HInstruction *add = InsertInstruction(
+  HInstruction* add = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
-  HInstruction *sub = InsertInstruction(
+  HInstruction* sub = InsertInstruction(
       new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
-  HInstruction *mul = InsertInstruction(
+  HInstruction* mul = InsertInstruction(
       new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
-  HInstruction *shl = InsertInstruction(
+  HInstruction* shl = InsertInstruction(
       new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
-  HInstruction *neg = InsertInstruction(
+  HInstruction* neg = InsertInstruction(
       new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
   PerformInductionVarAnalysis();
 
@@ -563,7 +602,7 @@
     k[d] = InsertLoopPhi(0, d);
   }
 
-  HInstruction *inc = InsertInstruction(
+  HInstruction* inc = InsertInstruction(
       new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
   HInstruction* store = InsertArrayStore(inc, 9);
 
@@ -597,7 +636,7 @@
   //   a[i] = 0;
   // }
   BuildLoopNest(1);
-  HInstruction *conv = InsertInstruction(
+  HInstruction* conv = InsertInstruction(
       new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
   HInstruction* store1 = InsertArrayStore(conv, 0);
   HInstruction* store2 = InsertArrayStore(basic_[0], 0);
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index cd8b7c7..140c7f0 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -525,6 +525,8 @@
             return GetMul(info->op_a, info->op_b, trip, in_body, is_min);
           case HInductionVarAnalysis::kDiv:
             return GetDiv(info->op_a, info->op_b, trip, in_body, is_min);
+          case HInductionVarAnalysis::kXor:
+            return GetXor(info->op_a, info->op_b);
           case HInductionVarAnalysis::kFetch:
             return GetFetch(info->fetch, trip, in_body, is_min);
           case HInductionVarAnalysis::kTripCountInLoop:
@@ -626,6 +628,21 @@
   return Value();
 }
 
+InductionVarRange::Value InductionVarRange::GetXor(
+    HInductionVarAnalysis::InductionInfo* info1,
+    HInductionVarAnalysis::InductionInfo* info2) const {
+  int64_t v1 = 0;
+  int64_t v2 = 0;
+  // Only accept exact values.
+  if (IsConstant(info1, kExact, &v1) && IsConstant(info2, kExact, &v2)) {
+    int64_t value = v1 ^ v2;
+    if (CanLongValueFitIntoInt(value)) {
+      return Value(static_cast<int32_t>(value));
+    }
+  }
+  return Value();
+}
+
 InductionVarRange::Value InductionVarRange::MulRangeAndConstant(
     int64_t value,
     HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index df31e81..8951300 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -193,6 +193,8 @@
                HInductionVarAnalysis::InductionInfo* trip,
                bool in_body,
                bool is_min) const;
+  Value GetXor(HInductionVarAnalysis::InductionInfo* info1,
+               HInductionVarAnalysis::InductionInfo* info2) const;
 
   Value MulRangeAndConstant(int64_t value,
                             HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 486626b..a1dcd58 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -38,7 +38,7 @@
            const DexCompilationUnit& outer_compilation_unit,
            const DexCompilationUnit& caller_compilation_unit,
            CompilerDriver* compiler_driver,
-           StackHandleScopeCollection* handles,
+           VariableSizedHandleScope* handles,
            OptimizingCompilerStats* stats,
            size_t total_number_of_dex_registers,
            size_t depth)
@@ -197,7 +197,7 @@
   const size_t total_number_of_dex_registers_;
   const size_t depth_;
   size_t number_of_inlined_instructions_;
-  StackHandleScopeCollection* const handles_;
+  VariableSizedHandleScope* const handles_;
 
   DISALLOW_COPY_AND_ASSIGN(HInliner);
 };
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 874c1ed..1e69966 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -35,7 +35,7 @@
 // double).
 static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == 0);
 
-void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) {
+void HGraph::InitializeInexactObjectRTI(VariableSizedHandleScope* handles) {
   ScopedObjectAccess soa(Thread::Current());
   // Create the inexact Object reference type and store it in the HGraph.
   ClassLinker* linker = Runtime::Current()->GetClassLinker();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 426c4f1..6f4f3c9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -336,7 +336,7 @@
   }
 
   // Acquires and stores RTI of inexact Object to be used when creating HNullConstant.
-  void InitializeInexactObjectRTI(StackHandleScopeCollection* handles);
+  void InitializeInexactObjectRTI(VariableSizedHandleScope* handles);
 
   ArenaAllocator* GetArena() const { return arena_; }
   const ArenaVector<HBasicBlock*>& GetBlocks() const { return blocks_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index d6f8307..4370a84 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -319,7 +319,7 @@
                         CompilerDriver* driver,
                         const DexCompilationUnit& dex_compilation_unit,
                         PassObserver* pass_observer,
-                        StackHandleScopeCollection* handles) const;
+                        VariableSizedHandleScope* handles) const;
 
   void RunOptimizations(HOptimization* optimizations[],
                         size_t length,
@@ -358,7 +358,7 @@
                        CompilerDriver* driver,
                        const DexCompilationUnit& dex_compilation_unit,
                        PassObserver* pass_observer,
-                       StackHandleScopeCollection* handles) const;
+                       VariableSizedHandleScope* handles) const;
 
   void RunArchOptimizations(InstructionSet instruction_set,
                             HGraph* graph,
@@ -442,7 +442,7 @@
     CodeGenerator* codegen,
     CompilerDriver* driver,
     const DexCompilationUnit& dex_compilation_unit,
-    StackHandleScopeCollection* handles,
+    VariableSizedHandleScope* handles,
     SideEffectsAnalysis* most_recent_side_effects,
     HInductionVarAnalysis* most_recent_induction) {
   std::string opt_name = ConvertPassNameToOptimizationName(pass_name);
@@ -524,7 +524,7 @@
     CodeGenerator* codegen,
     CompilerDriver* driver,
     const DexCompilationUnit& dex_compilation_unit,
-    StackHandleScopeCollection* handles) {
+    VariableSizedHandleScope* handles) {
   // Few HOptimizations constructors require SideEffectsAnalysis or HInductionVarAnalysis
   // instances. This method assumes that each of them expects the nearest instance preceeding it
   // in the pass name list.
@@ -570,7 +570,7 @@
                                          CompilerDriver* driver,
                                          const DexCompilationUnit& dex_compilation_unit,
                                          PassObserver* pass_observer,
-                                         StackHandleScopeCollection* handles) const {
+                                         VariableSizedHandleScope* handles) const {
   OptimizingCompilerStats* stats = compilation_stats_.get();
   const CompilerOptions& compiler_options = driver->GetCompilerOptions();
   bool should_inline = (compiler_options.GetInlineDepthLimit() > 0)
@@ -707,7 +707,7 @@
                                           CompilerDriver* driver,
                                           const DexCompilationUnit& dex_compilation_unit,
                                           PassObserver* pass_observer,
-                                          StackHandleScopeCollection* handles) const {
+                                          VariableSizedHandleScope* handles) const {
   OptimizingCompilerStats* stats = compilation_stats_.get();
   ArenaAllocator* arena = graph->GetArena();
   if (driver->GetCompilerOptions().GetPassesToRun() != nullptr) {
@@ -949,7 +949,7 @@
 
   {
     ScopedObjectAccess soa(Thread::Current());
-    StackHandleScopeCollection handles(soa.Self());
+    VariableSizedHandleScope handles(soa.Self());
     // Do not hold `mutator_lock_` between optimizations.
     ScopedThreadSuspension sts(soa.Self(), kNative);
 
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 2a23c92..58d9017 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -90,7 +90,7 @@
 
   {
     ScopedObjectAccess soa(Thread::Current());
-    StackHandleScopeCollection handles(soa.Self());
+    VariableSizedHandleScope handles(soa.Self());
     HGraphBuilder builder(graph, *item, &handles, return_type);
     bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
     return graph_built ? graph : nullptr;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 45a3ce4..83698ad 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -35,7 +35,7 @@
   }
 }
 
-static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollection* handles,
+static inline ReferenceTypeInfo::TypeHandle GetRootHandle(VariableSizedHandleScope* handles,
                                                           ClassLinker::ClassRoot class_root,
                                                           ReferenceTypeInfo::TypeHandle* cache) {
   if (!ReferenceTypeInfo::IsValidHandle(*cache)) {
@@ -109,7 +109,7 @@
 
 ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
                                                    Handle<mirror::DexCache> hint_dex_cache,
-                                                   StackHandleScopeCollection* handles,
+                                                   VariableSizedHandleScope* handles,
                                                    bool is_first_run,
                                                    const char* name)
     : HOptimization(graph, name),
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 61428b2..4663471 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -34,7 +34,7 @@
  public:
   ReferenceTypePropagation(HGraph* graph,
                            Handle<mirror::DexCache> hint_dex_cache,
-                           StackHandleScopeCollection* handles,
+                           VariableSizedHandleScope* handles,
                            bool is_first_run,
                            const char* name = kReferenceTypePropagationPassName);
 
@@ -56,7 +56,7 @@
  private:
   class HandleCache {
    public:
-    explicit HandleCache(StackHandleScopeCollection* handles) : handles_(handles) { }
+    explicit HandleCache(VariableSizedHandleScope* handles) : handles_(handles) { }
 
     template <typename T>
     MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -74,7 +74,7 @@
     ReferenceTypeInfo::TypeHandle GetThrowableClassHandle();
 
    private:
-    StackHandleScopeCollection* handles_;
+    VariableSizedHandleScope* handles_;
 
     ReferenceTypeInfo::TypeHandle object_class_handle_;
     ReferenceTypeInfo::TypeHandle class_class_handle_;
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 75a4eac..b061c87 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -35,7 +35,7 @@
 
   ~ReferenceTypePropagationTest() { }
 
-  void SetupPropagation(StackHandleScopeCollection* handles) {
+  void SetupPropagation(VariableSizedHandleScope* handles) {
     graph_->InitializeInexactObjectRTI(handles);
     propagation_ = new (&allocator_) ReferenceTypePropagation(graph_,
                                                               Handle<mirror::DexCache>(),
@@ -79,7 +79,7 @@
 
 TEST_F(ReferenceTypePropagationTest, ProperSetup) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScopeCollection handles(soa.Self());
+  VariableSizedHandleScope handles(soa.Self());
   SetupPropagation(&handles);
 
   EXPECT_TRUE(propagation_ != nullptr);
@@ -88,7 +88,7 @@
 
 TEST_F(ReferenceTypePropagationTest, MergeInvalidTypes) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScopeCollection handles(soa.Self());
+  VariableSizedHandleScope handles(soa.Self());
   SetupPropagation(&handles);
 
   // Two invalid types.
@@ -120,7 +120,7 @@
 
 TEST_F(ReferenceTypePropagationTest, MergeValidTypes) {
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScopeCollection handles(soa.Self());
+  VariableSizedHandleScope handles(soa.Self());
   SetupPropagation(&handles);
 
   // Same types.
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index d7360ad..45dac54 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -49,7 +49,7 @@
  public:
   SsaBuilder(HGraph* graph,
              Handle<mirror::DexCache> dex_cache,
-             StackHandleScopeCollection* handles)
+             VariableSizedHandleScope* handles)
       : graph_(graph),
         dex_cache_(dex_cache),
         handles_(handles),
@@ -116,7 +116,7 @@
 
   HGraph* graph_;
   Handle<mirror::DexCache> dex_cache_;
-  StackHandleScopeCollection* const handles_;
+  VariableSizedHandleScope* const handles_;
 
   // True if types of ambiguous ArrayGets have been resolved.
   bool agets_fixed_;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 8a51dc2..5ec9898 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -274,7 +274,7 @@
       AbortF("field operation on NULL object: %p", java_object);
       return false;
     }
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o.Ptr())) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("field operation on invalid %s: %p",
              ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
@@ -782,7 +782,7 @@
       }
     }
 
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
+    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj.Ptr())) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("%s is an invalid %s: %p (%p)",
              what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
@@ -939,7 +939,7 @@
         ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc);
         if (c == nullptr) {
           *msg += "NULL";
-        } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+        } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c.Ptr())) {
           StringAppendF(msg, "INVALID POINTER:%p", jc);
         } else if (!c->IsClass()) {
           *msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
@@ -1108,7 +1108,7 @@
     }
 
     ObjPtr<mirror::Array> a = soa.Decode<mirror::Array>(java_array);
-    if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) {
+    if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a.Ptr()))) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("jarray is an invalid %s: %p (%p)",
              ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(),
@@ -1145,7 +1145,7 @@
     }
     ArtField* f = soa.DecodeField(fid);
     // TODO: Better check here.
-    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass())) {
+    if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass().Ptr())) {
       Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
       AbortF("invalid jfieldID: %p", fid);
       return nullptr;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7006f70..ada1a23 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -892,15 +892,16 @@
                                        std::vector<uint64_t>* counts) {
   gc::Heap* heap = Runtime::Current()->GetHeap();
   heap->CollectGarbage(false);
-  std::vector<mirror::Class*> classes;
+  VariableSizedHandleScope hs(Thread::Current());
+  std::vector<Handle<mirror::Class>> classes;
   counts->clear();
   for (size_t i = 0; i < class_ids.size(); ++i) {
     JDWP::JdwpError error;
-    mirror::Class* c = DecodeClass(class_ids[i], &error);
+    ObjPtr<mirror::Class> c = DecodeClass(class_ids[i], &error);
     if (c == nullptr) {
       return error;
     }
-    classes.push_back(c);
+    classes.push_back(hs.NewHandle(c));
     counts->push_back(0);
   }
   heap->CountInstances(classes, false, &(*counts)[0]);
@@ -913,14 +914,15 @@
   // We only want reachable instances, so do a GC.
   heap->CollectGarbage(false);
   JDWP::JdwpError error;
-  mirror::Class* c = DecodeClass(class_id, &error);
+  ObjPtr<mirror::Class> c = DecodeClass(class_id, &error);
   if (c == nullptr) {
     return error;
   }
-  std::vector<mirror::Object*> raw_instances;
-  Runtime::Current()->GetHeap()->GetInstances(c, max_count, raw_instances);
+  VariableSizedHandleScope hs(Thread::Current());
+  std::vector<Handle<mirror::Object>> raw_instances;
+  Runtime::Current()->GetHeap()->GetInstances(hs, hs.NewHandle(c), max_count, raw_instances);
   for (size_t i = 0; i < raw_instances.size(); ++i) {
-    instances->push_back(gRegistry->Add(raw_instances[i]));
+    instances->push_back(gRegistry->Add(raw_instances[i].Get()));
   }
   return JDWP::ERR_NONE;
 }
@@ -930,14 +932,15 @@
   gc::Heap* heap = Runtime::Current()->GetHeap();
   heap->CollectGarbage(false);
   JDWP::JdwpError error;
-  mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+  ObjPtr<mirror::Object> o = gRegistry->Get<mirror::Object*>(object_id, &error);
   if (o == nullptr) {
     return JDWP::ERR_INVALID_OBJECT;
   }
-  std::vector<mirror::Object*> raw_instances;
-  heap->GetReferringObjects(o, max_count, raw_instances);
+  VariableSizedHandleScope hs(Thread::Current());
+  std::vector<Handle<mirror::Object>> raw_instances;
+  heap->GetReferringObjects(hs, hs.NewHandle(o), max_count, raw_instances);
   for (size_t i = 0; i < raw_instances.size(); ++i) {
-    referring_objects->push_back(gRegistry->Add(raw_instances[i]));
+    referring_objects->push_back(gRegistry->Add(raw_instances[i].Get()));
   }
   return JDWP::ERR_NONE;
 }
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 7c7e2da..20fa0d8 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -144,7 +144,7 @@
     HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&o));
     CheckReferenceResult(h_obj, self);
   }
-  VerifyObject(o.Ptr());
+  VerifyObject(o);
   return o.Ptr();
 }
 
diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h
index 6fb74d3..f60bc0c 100644
--- a/runtime/gc/allocation_listener.h
+++ b/runtime/gc/allocation_listener.h
@@ -22,6 +22,7 @@
 
 #include "base/macros.h"
 #include "base/mutex.h"
+#include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "gc_root.h"
 
@@ -39,7 +40,7 @@
  public:
   virtual ~AllocationListener() {}
 
-  virtual void ObjectAllocated(Thread* self, mirror::Object** obj, size_t byte_count)
+  virtual void ObjectAllocated(Thread* self, ObjPtr<mirror::Object>* obj, size_t byte_count)
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 };
 
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 13ebb27..d921900 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -19,6 +19,7 @@
 #include "art_method-inl.h"
 #include "base/enums.h"
 #include "base/stl_util.h"
+#include "obj_ptr-inl.h"
 #include "stack.h"
 
 #ifdef ART_TARGET_ANDROID
@@ -263,7 +264,7 @@
 }
 
 void AllocRecordObjectMap::RecordAllocation(Thread* self,
-                                            mirror::Object** obj,
+                                            ObjPtr<mirror::Object>* obj,
                                             size_t byte_count) {
   // Get stack trace outside of lock in case there are allocations during the stack walk.
   // b/27858645.
@@ -305,7 +306,7 @@
   trace.SetTid(self->GetTid());
 
   // Add the record.
-  Put(*obj, AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace)));
+  Put(obj->Ptr(), AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace)));
   DCHECK_LE(Size(), alloc_record_max_);
 }
 
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index f1f013b..c8b2b89 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -21,6 +21,7 @@
 #include <memory>
 
 #include "base/mutex.h"
+#include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "gc_root.h"
 
@@ -210,7 +211,7 @@
   // Caller needs to check that it is enabled before calling since we read the stack trace before
   // checking the enabled boolean.
   void RecordAllocation(Thread* self,
-                        mirror::Object** obj,
+                        ObjPtr<mirror::Object>* obj,
                         size_t byte_count)
       REQUIRES(!Locks::alloc_tracker_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 2e97172..76a478e 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -296,7 +296,6 @@
       REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
     if (from_space_->HasAddress(ref)) {
-      Runtime::Current()->GetHeap()->DumpObject(LOG_STREAM(INFO), obj);
       LOG(FATAL) << ref << " found in from space";
     }
   }
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 00adefb..05ce9c7 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -41,7 +41,7 @@
 
 template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
 inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
-                                                      mirror::Class* klass,
+                                                      ObjPtr<mirror::Class> klass,
                                                       size_t byte_count,
                                                       AllocatorType allocator,
                                                       const PreFenceVisitor& pre_fence_visitor) {
@@ -52,16 +52,19 @@
     CHECK_EQ(self->GetState(), kRunnable);
     self->AssertThreadSuspensionIsAllowable();
     self->AssertNoPendingException();
+    // Make sure to preserve klass.
+    StackHandleScope<1> hs(self);
+    HandleWrapperObjPtr<mirror::Class> h = hs.NewHandleWrapper(&klass);
     self->PoisonObjectPointers();
   }
   // Need to check that we arent the large object allocator since the large object allocation code
   // path this function. If we didn't check we would have an infinite loop.
-  mirror::Object* obj;
+  ObjPtr<mirror::Object> obj;
   if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
     obj = AllocLargeObject<kInstrumented, PreFenceVisitor>(self, &klass, byte_count,
                                                            pre_fence_visitor);
     if (obj != nullptr) {
-      return obj;
+      return obj.Ptr();
     } else {
       // There should be an OOM exception, since we are retrying, clear it.
       self->ClearException();
@@ -85,7 +88,7 @@
     obj->SetClass(klass);
     if (kUseBakerOrBrooksReadBarrier) {
       if (kUseBrooksReadBarrier) {
-        obj->SetReadBarrierPointer(obj);
+        obj->SetReadBarrierPointer(obj.Ptr());
       }
       obj->AssertReadBarrierPointer();
     }
@@ -93,14 +96,15 @@
     usable_size = bytes_allocated;
     pre_fence_visitor(obj, usable_size);
     QuasiAtomic::ThreadFenceForConstructor();
-  } else if (!kInstrumented && allocator == kAllocatorTypeRosAlloc &&
-             (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) &&
-             LIKELY(obj != nullptr)) {
+  } else if (
+      !kInstrumented && allocator == kAllocatorTypeRosAlloc &&
+      (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) != nullptr &&
+      LIKELY(obj != nullptr)) {
     DCHECK(!is_running_on_memory_tool_);
     obj->SetClass(klass);
     if (kUseBakerOrBrooksReadBarrier) {
       if (kUseBrooksReadBarrier) {
-        obj->SetReadBarrierPointer(obj);
+        obj->SetReadBarrierPointer(obj.Ptr());
       }
       obj->AssertReadBarrierPointer();
     }
@@ -141,7 +145,7 @@
     obj->SetClass(klass);
     if (kUseBakerOrBrooksReadBarrier) {
       if (kUseBrooksReadBarrier) {
-        obj->SetReadBarrierPointer(obj);
+        obj->SetReadBarrierPointer(obj.Ptr());
       }
       obj->AssertReadBarrierPointer();
     }
@@ -213,25 +217,25 @@
   }
   VerifyObject(obj);
   self->VerifyStack();
-  return obj;
+  return obj.Ptr();
 }
 
 // The size of a thread-local allocation stack in the number of references.
 static constexpr size_t kThreadLocalAllocationStackSize = 128;
 
-inline void Heap::PushOnAllocationStack(Thread* self, mirror::Object** obj) {
+inline void Heap::PushOnAllocationStack(Thread* self, ObjPtr<mirror::Object>* obj) {
   if (kUseThreadLocalAllocationStack) {
-    if (UNLIKELY(!self->PushOnThreadLocalAllocationStack(*obj))) {
+    if (UNLIKELY(!self->PushOnThreadLocalAllocationStack(obj->Ptr()))) {
       PushOnThreadLocalAllocationStackWithInternalGC(self, obj);
     }
-  } else if (UNLIKELY(!allocation_stack_->AtomicPushBack(*obj))) {
+  } else if (UNLIKELY(!allocation_stack_->AtomicPushBack(obj->Ptr()))) {
     PushOnAllocationStackWithInternalGC(self, obj);
   }
 }
 
 template <bool kInstrumented, typename PreFenceVisitor>
 inline mirror::Object* Heap::AllocLargeObject(Thread* self,
-                                              mirror::Class** klass,
+                                              ObjPtr<mirror::Class>* klass,
                                               size_t byte_count,
                                               const PreFenceVisitor& pre_fence_visitor) {
   // Save and restore the class in case it moves.
@@ -405,7 +409,7 @@
   return ret;
 }
 
-inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const {
+inline bool Heap::ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const {
   // We need to have a zygote space or else our newly allocated large object can end up in the
   // Zygote resulting in it being prematurely freed.
   // We can only do this for primitive objects since large objects will not be within the card table
@@ -435,7 +439,7 @@
 
 inline void Heap::CheckConcurrentGC(Thread* self,
                                     size_t new_num_bytes_allocated,
-                                    mirror::Object** obj) {
+                                    ObjPtr<mirror::Object>* obj) {
   if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) {
     RequestConcurrentGCAndSaveObject(self, false, obj);
   }
@@ -447,6 +451,16 @@
   card_table_->MarkCard(dst.Ptr());
 }
 
+inline void Heap::WriteBarrierArray(ObjPtr<mirror::Object> dst,
+                                    int start_offset ATTRIBUTE_UNUSED,
+                                    size_t length ATTRIBUTE_UNUSED) {
+  card_table_->MarkCard(dst.Ptr());
+}
+
+inline void Heap::WriteBarrierEveryFieldOf(ObjPtr<mirror::Object> obj) {
+  card_table_->MarkCard(obj.Ptr());
+}
+
 }  // namespace gc
 }  // namespace art
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 33f849a..db90a2a 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -47,6 +47,7 @@
 #include "gc/collector/semi_space.h"
 #include "gc/collector/sticky_mark_sweep.h"
 #include "gc/reference_processor.h"
+#include "gc/scoped_gc_critical_section.h"
 #include "gc/space/bump_pointer_space.h"
 #include "gc/space/dlmalloc_space-inl.h"
 #include "gc/space/image_space.h"
@@ -760,83 +761,6 @@
   }
 }
 
-std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) {
-  if (!IsValidContinuousSpaceObjectAddress(klass)) {
-    return StringPrintf("<non heap address klass %p>", klass);
-  }
-  mirror::Class* component_type = klass->GetComponentType<kVerifyNone>();
-  if (IsValidContinuousSpaceObjectAddress(component_type) && klass->IsArrayClass<kVerifyNone>()) {
-    std::string result("[");
-    result += SafeGetClassDescriptor(component_type);
-    return result;
-  } else if (UNLIKELY(klass->IsPrimitive<kVerifyNone>())) {
-    return Primitive::Descriptor(klass->GetPrimitiveType<kVerifyNone>());
-  } else if (UNLIKELY(klass->IsProxyClass<kVerifyNone>())) {
-    return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(klass);
-  } else {
-    mirror::DexCache* dex_cache = klass->GetDexCache<kVerifyNone>();
-    if (!IsValidContinuousSpaceObjectAddress(dex_cache)) {
-      return StringPrintf("<non heap address dex_cache %p>", dex_cache);
-    }
-    const DexFile* dex_file = dex_cache->GetDexFile();
-    uint16_t class_def_idx = klass->GetDexClassDefIndex();
-    if (class_def_idx == DexFile::kDexNoIndex16) {
-      return "<class def not found>";
-    }
-    const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
-    const DexFile::TypeId& type_id = dex_file->GetTypeId(class_def.class_idx_);
-    return dex_file->GetTypeDescriptor(type_id);
-  }
-}
-
-std::string Heap::SafePrettyTypeOf(mirror::Object* obj) {
-  if (obj == nullptr) {
-    return "null";
-  }
-  mirror::Class* klass = obj->GetClass<kVerifyNone>();
-  if (klass == nullptr) {
-    return "(class=null)";
-  }
-  std::string result(SafeGetClassDescriptor(klass));
-  if (obj->IsClass()) {
-    result += "<" + SafeGetClassDescriptor(obj->AsClass<kVerifyNone>()) + ">";
-  }
-  return result;
-}
-
-void Heap::DumpObject(std::ostream& stream, mirror::Object* obj) {
-  if (obj == nullptr) {
-    stream << "(obj=null)";
-    return;
-  }
-  if (IsAligned<kObjectAlignment>(obj)) {
-    space::Space* space = nullptr;
-    // Don't use find space since it only finds spaces which actually contain objects instead of
-    // spaces which may contain objects (e.g. cleared bump pointer spaces).
-    for (const auto& cur_space : continuous_spaces_) {
-      if (cur_space->HasAddress(obj)) {
-        space = cur_space;
-        break;
-      }
-    }
-    // Unprotect all the spaces.
-    for (const auto& con_space : continuous_spaces_) {
-      mprotect(con_space->Begin(), con_space->Capacity(), PROT_READ | PROT_WRITE);
-    }
-    stream << "Object " << obj;
-    if (space != nullptr) {
-      stream << " in space " << *space;
-    }
-    mirror::Class* klass = obj->GetClass<kVerifyNone>();
-    stream << "\nclass=" << klass;
-    if (klass != nullptr) {
-      stream << " type= " << SafePrettyTypeOf(obj);
-    }
-    // Re-protect the address we faulted on.
-    mprotect(AlignDown(obj, kPageSize), kPageSize, PROT_NONE);
-  }
-}
-
 bool Heap::IsCompilingBoot() const {
   if (!Runtime::Current()->IsAotCompiler()) {
     return false;
@@ -1012,10 +936,14 @@
     }
     DecrementDisableMovingGC(self);
   } else {
+    // Since concurrent moving GC has thread suspension, also poison ObjPtr the normal case to
+    // catch bugs.
+    self->PoisonObjectPointers();
     // GCs can move objects, so don't allow this.
     ScopedAssertNoThreadSuspension ants("Visiting objects");
     DCHECK(region_space_ == nullptr);
     VisitObjectsInternal(callback, arg);
+    self->PoisonObjectPointers();
   }
 }
 
@@ -1325,33 +1253,42 @@
   VLOG(heap) << "Finished ~Heap()";
 }
 
-space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(const mirror::Object* obj,
-                                                            bool fail_ok) const {
+
+space::ContinuousSpace* Heap::FindContinuousSpaceFromAddress(const mirror::Object* addr) const {
   for (const auto& space : continuous_spaces_) {
-    if (space->Contains(obj)) {
+    if (space->Contains(addr)) {
       return space;
     }
   }
-  if (!fail_ok) {
-    LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!";
-  }
   return nullptr;
 }
 
-space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(const mirror::Object* obj,
+space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(ObjPtr<mirror::Object> obj,
+                                                            bool fail_ok) const {
+  space::ContinuousSpace* space = FindContinuousSpaceFromAddress(obj.Ptr());
+  if (space != nullptr) {
+    return space;
+  }
+  if (!fail_ok) {
+    LOG(FATAL) << "object " << obj << " not inside any spaces!";
+  }
+  return nullptr;
+}
+
+space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(ObjPtr<mirror::Object> obj,
                                                                   bool fail_ok) const {
   for (const auto& space : discontinuous_spaces_) {
-    if (space->Contains(obj)) {
+    if (space->Contains(obj.Ptr())) {
       return space;
     }
   }
   if (!fail_ok) {
-    LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!";
+    LOG(FATAL) << "object " << obj << " not inside any spaces!";
   }
   return nullptr;
 }
 
-space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) const {
+space::Space* Heap::FindSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const {
   space::Space* result = FindContinuousSpaceFromObject(obj, true);
   if (result != nullptr) {
     return result;
@@ -1359,6 +1296,21 @@
   return FindDiscontinuousSpaceFromObject(obj, fail_ok);
 }
 
+space::Space* Heap::FindSpaceFromAddress(const void* addr) const {
+  for (const auto& space : continuous_spaces_) {
+    if (space->Contains(reinterpret_cast<const mirror::Object*>(addr))) {
+      return space;
+    }
+  }
+  for (const auto& space : discontinuous_spaces_) {
+    if (space->Contains(reinterpret_cast<const mirror::Object*>(addr))) {
+      return space;
+    }
+  }
+  return nullptr;
+}
+
+
 void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
   // If we're in a stack overflow, do not create a new exception. It would require running the
   // constructor, which will of course still be in a stack overflow.
@@ -1423,6 +1375,8 @@
     // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
     // about pauses.
     ScopedTrace trace("Deflating monitors");
+    // Avoid race conditions on the lock word for CC.
+    ScopedGCCriticalSection gcs(self, kGcCauseTrim, kCollectorTypeHeapTrim);
     ScopedSuspendAll ssa(__FUNCTION__);
     uint64_t start_time = NanoTime();
     size_t count = runtime->GetMonitorList()->DeflateMonitors();
@@ -1523,62 +1477,49 @@
       << static_cast<int>(100 * managed_utilization) << "%.";
 }
 
-bool Heap::IsValidObjectAddress(ObjPtr<mirror::Object> obj) const {
-  // Note: we deliberately don't take the lock here, and mustn't test anything that would require
-  // taking the lock.
-  if (obj == nullptr) {
+bool Heap::IsValidObjectAddress(const void* addr) const {
+  if (addr == nullptr) {
     return true;
   }
-  return IsAligned<kObjectAlignment>(obj.Ptr()) &&
-      FindSpaceFromObject(obj.Ptr(), true) != nullptr;
+  return IsAligned<kObjectAlignment>(addr) && FindSpaceFromAddress(addr) != nullptr;
 }
 
-bool Heap::IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const {
-  return FindContinuousSpaceFromObject(obj, true) != nullptr;
+bool Heap::IsNonDiscontinuousSpaceHeapAddress(const void* addr) const {
+  return FindContinuousSpaceFromAddress(reinterpret_cast<const mirror::Object*>(addr)) != nullptr;
 }
 
-bool Heap::IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const {
-  if (obj == nullptr || !IsAligned<kObjectAlignment>(obj)) {
+bool Heap::IsLiveObjectLocked(ObjPtr<mirror::Object> obj,
+                              bool search_allocation_stack,
+                              bool search_live_stack,
+                              bool sorted) {
+  if (UNLIKELY(!IsAligned<kObjectAlignment>(obj.Ptr()))) {
     return false;
   }
-  for (const auto& space : continuous_spaces_) {
-    if (space->HasAddress(obj)) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack,
-                              bool search_live_stack, bool sorted) {
-  if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) {
-    return false;
-  }
-  if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj)) {
+  if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj.Ptr())) {
     mirror::Class* klass = obj->GetClass<kVerifyNone>();
     if (obj == klass) {
       // This case happens for java.lang.Class.
       return true;
     }
     return VerifyClassClass(klass) && IsLiveObjectLocked(klass);
-  } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj)) {
+  } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj.Ptr())) {
     // If we are in the allocated region of the temp space, then we are probably live (e.g. during
     // a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained.
-    return temp_space_->Contains(obj);
+    return temp_space_->Contains(obj.Ptr());
   }
-  if (region_space_ != nullptr && region_space_->HasAddress(obj)) {
+  if (region_space_ != nullptr && region_space_->HasAddress(obj.Ptr())) {
     return true;
   }
   space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true);
   space::DiscontinuousSpace* d_space = nullptr;
   if (c_space != nullptr) {
-    if (c_space->GetLiveBitmap()->Test(obj)) {
+    if (c_space->GetLiveBitmap()->Test(obj.Ptr())) {
       return true;
     }
   } else {
     d_space = FindDiscontinuousSpaceFromObject(obj, true);
     if (d_space != nullptr) {
-      if (d_space->GetLiveBitmap()->Test(obj)) {
+      if (d_space->GetLiveBitmap()->Test(obj.Ptr())) {
         return true;
       }
     }
@@ -1590,20 +1531,20 @@
     }
     if (search_allocation_stack) {
       if (sorted) {
-        if (allocation_stack_->ContainsSorted(obj)) {
+        if (allocation_stack_->ContainsSorted(obj.Ptr())) {
           return true;
         }
-      } else if (allocation_stack_->Contains(obj)) {
+      } else if (allocation_stack_->Contains(obj.Ptr())) {
         return true;
       }
     }
 
     if (search_live_stack) {
       if (sorted) {
-        if (live_stack_->ContainsSorted(obj)) {
+        if (live_stack_->ContainsSorted(obj.Ptr())) {
           return true;
         }
-      } else if (live_stack_->Contains(obj)) {
+      } else if (live_stack_->Contains(obj.Ptr())) {
         return true;
       }
     }
@@ -1611,12 +1552,12 @@
   // We need to check the bitmaps again since there is a race where we mark something as live and
   // then clear the stack containing it.
   if (c_space != nullptr) {
-    if (c_space->GetLiveBitmap()->Test(obj)) {
+    if (c_space->GetLiveBitmap()->Test(obj.Ptr())) {
       return true;
     }
   } else {
     d_space = FindDiscontinuousSpaceFromObject(obj, true);
-    if (d_space != nullptr && d_space->GetLiveBitmap()->Test(obj)) {
+    if (d_space != nullptr && d_space->GetLiveBitmap()->Test(obj.Ptr())) {
       return true;
     }
   }
@@ -1646,7 +1587,7 @@
   }
 }
 
-void Heap::VerifyObjectBody(mirror::Object* obj) {
+void Heap::VerifyObjectBody(ObjPtr<mirror::Object> obj) {
   if (verify_object_mode_ == kVerifyObjectModeDisabled) {
     return;
   }
@@ -1655,7 +1596,7 @@
   if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_.LoadRelaxed()) < 10 * KB)) {
     return;
   }
-  CHECK_ALIGNED(obj, kObjectAlignment) << "Object isn't aligned";
+  CHECK_ALIGNED(obj.Ptr(), kObjectAlignment) << "Object isn't aligned";
   mirror::Class* c = obj->GetFieldObject<mirror::Class, kVerifyNone>(mirror::Object::ClassOffset());
   CHECK(c != nullptr) << "Null class in object " << obj;
   CHECK_ALIGNED(c, kObjectAlignment) << "Class " << c << " not aligned in object " << obj;
@@ -1734,14 +1675,13 @@
                                              size_t* bytes_allocated,
                                              size_t* usable_size,
                                              size_t* bytes_tl_bulk_allocated,
-                                             mirror::Class** klass) {
+                                             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);
   StackHandleScope<1> hs(self);
-  HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(klass));
-  klass = nullptr;  // Invalidate for safety.
+  HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(klass));
   // The allocation failed. If the GC is running, block until it completes, and then retry the
   // allocation.
   collector::GcType last_gc = WaitForGcToComplete(kGcCauseForAlloc, self);
@@ -1944,7 +1884,7 @@
 
 class InstanceCounter {
  public:
-  InstanceCounter(const std::vector<mirror::Class*>& classes,
+  InstanceCounter(const std::vector<Handle<mirror::Class>>& classes,
                   bool use_is_assignable_from,
                   uint64_t* counts)
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -1956,7 +1896,7 @@
     mirror::Class* instance_class = obj->GetClass();
     CHECK(instance_class != nullptr);
     for (size_t i = 0; i < instance_counter->classes_.size(); ++i) {
-      mirror::Class* klass = instance_counter->classes_[i];
+      ObjPtr<mirror::Class> klass = instance_counter->classes_[i].Get();
       if (instance_counter->use_is_assignable_from_) {
         if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
           ++instance_counter->counts_[i];
@@ -1968,13 +1908,14 @@
   }
 
  private:
-  const std::vector<mirror::Class*>& classes_;
+  const std::vector<Handle<mirror::Class>>& classes_;
   bool use_is_assignable_from_;
   uint64_t* const counts_;
   DISALLOW_COPY_AND_ASSIGN(InstanceCounter);
 };
 
-void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from,
+void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes,
+                          bool use_is_assignable_from,
                           uint64_t* counts) {
   InstanceCounter counter(classes, use_is_assignable_from, counts);
   VisitObjects(InstanceCounter::Callback, &counter);
@@ -1982,44 +1923,55 @@
 
 class InstanceCollector {
  public:
-  InstanceCollector(mirror::Class* c, int32_t max_count, std::vector<mirror::Object*>& instances)
+  InstanceCollector(VariableSizedHandleScope& scope,
+                    Handle<mirror::Class> c,
+                    int32_t max_count,
+                    std::vector<Handle<mirror::Object>>& instances)
       REQUIRES_SHARED(Locks::mutator_lock_)
-      : class_(c), max_count_(max_count), instances_(instances) {
-  }
+      : scope_(scope),
+        class_(c),
+        max_count_(max_count),
+        instances_(instances) {}
+
   static void Callback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
     DCHECK(arg != nullptr);
     InstanceCollector* instance_collector = reinterpret_cast<InstanceCollector*>(arg);
-    if (obj->GetClass() == instance_collector->class_) {
+    if (obj->GetClass() == instance_collector->class_.Get()) {
       if (instance_collector->max_count_ == 0 ||
           instance_collector->instances_.size() < instance_collector->max_count_) {
-        instance_collector->instances_.push_back(obj);
+        instance_collector->instances_.push_back(instance_collector->scope_.NewHandle(obj));
       }
     }
   }
 
  private:
-  const mirror::Class* const class_;
+  VariableSizedHandleScope& scope_;
+  Handle<mirror::Class> const class_;
   const uint32_t max_count_;
-  std::vector<mirror::Object*>& instances_;
+  std::vector<Handle<mirror::Object>>& instances_;
   DISALLOW_COPY_AND_ASSIGN(InstanceCollector);
 };
 
-void Heap::GetInstances(mirror::Class* c,
+void Heap::GetInstances(VariableSizedHandleScope& scope,
+                        Handle<mirror::Class> c,
                         int32_t max_count,
-                        std::vector<mirror::Object*>& instances) {
-  InstanceCollector collector(c, max_count, instances);
+                        std::vector<Handle<mirror::Object>>& instances) {
+  InstanceCollector collector(scope, c, max_count, instances);
   VisitObjects(&InstanceCollector::Callback, &collector);
 }
 
 class ReferringObjectsFinder {
  public:
-  ReferringObjectsFinder(mirror::Object* object,
+  ReferringObjectsFinder(VariableSizedHandleScope& scope,
+                         Handle<mirror::Object> object,
                          int32_t max_count,
-                         std::vector<mirror::Object*>& referring_objects)
+                         std::vector<Handle<mirror::Object>>& referring_objects)
       REQUIRES_SHARED(Locks::mutator_lock_)
-      : object_(object), max_count_(max_count), referring_objects_(referring_objects) {
-  }
+      : scope_(scope),
+        object_(object),
+        max_count_(max_count),
+        referring_objects_(referring_objects) {}
 
   static void Callback(mirror::Object* obj, void* arg)
       REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
@@ -2029,16 +1981,18 @@
   // For bitmap Visit.
   // TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
   // annotalysis on visitors.
-  void operator()(mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS {
+  void operator()(ObjPtr<mirror::Object> o) const NO_THREAD_SAFETY_ANALYSIS {
     o->VisitReferences(*this, VoidFunctor());
   }
 
   // For Object::VisitReferences.
-  void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<mirror::Object> obj,
+                  MemberOffset offset,
+                  bool is_static ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
-    if (ref == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
-      referring_objects_.push_back(obj);
+    if (ref == object_.Get() && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
+      referring_objects_.push_back(scope_.NewHandle(obj));
     }
   }
 
@@ -2047,15 +2001,18 @@
   void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
 
  private:
-  const mirror::Object* const object_;
+  VariableSizedHandleScope& scope_;
+  Handle<mirror::Object> const object_;
   const uint32_t max_count_;
-  std::vector<mirror::Object*>& referring_objects_;
+  std::vector<Handle<mirror::Object>>& referring_objects_;
   DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder);
 };
 
-void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count,
-                               std::vector<mirror::Object*>& referring_objects) {
-  ReferringObjectsFinder finder(o, max_count, referring_objects);
+void Heap::GetReferringObjects(VariableSizedHandleScope& scope,
+                               Handle<mirror::Object> o,
+                               int32_t max_count,
+                               std::vector<Handle<mirror::Object>>& referring_objects) {
+  ReferringObjectsFinder finder(scope, o, max_count, referring_objects);
   VisitObjects(&ReferringObjectsFinder::Callback, &finder);
 }
 
@@ -3113,41 +3070,42 @@
   const bool verify_referent_;
 };
 
-void Heap::PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) {
+void Heap::PushOnAllocationStackWithInternalGC(Thread* self, ObjPtr<mirror::Object>* obj) {
   // Slow path, the allocation stack push back must have already failed.
-  DCHECK(!allocation_stack_->AtomicPushBack(*obj));
+  DCHECK(!allocation_stack_->AtomicPushBack(obj->Ptr()));
   do {
     // TODO: Add handle VerifyObject.
     StackHandleScope<1> hs(self);
-    HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
+    HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
     // Push our object into the reserve region of the allocaiton stack. This is only required due
     // to heap verification requiring that roots are live (either in the live bitmap or in the
     // allocation stack).
-    CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(*obj));
+    CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(obj->Ptr()));
     CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
-  } while (!allocation_stack_->AtomicPushBack(*obj));
+  } while (!allocation_stack_->AtomicPushBack(obj->Ptr()));
 }
 
-void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) {
+void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self,
+                                                          ObjPtr<mirror::Object>* obj) {
   // Slow path, the allocation stack push back must have already failed.
-  DCHECK(!self->PushOnThreadLocalAllocationStack(*obj));
+  DCHECK(!self->PushOnThreadLocalAllocationStack(obj->Ptr()));
   StackReference<mirror::Object>* start_address;
   StackReference<mirror::Object>* end_address;
   while (!allocation_stack_->AtomicBumpBack(kThreadLocalAllocationStackSize, &start_address,
                                             &end_address)) {
     // TODO: Add handle VerifyObject.
     StackHandleScope<1> hs(self);
-    HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
+    HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
     // Push our object into the reserve region of the allocaiton stack. This is only required due
     // to heap verification requiring that roots are live (either in the live bitmap or in the
     // allocation stack).
-    CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(*obj));
+    CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(obj->Ptr()));
     // Push into the reserve allocation stack.
     CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
   }
   self->SetThreadLocalAllocationStack(start_address, end_address);
   // Retry on the new thread-local allocation stack.
-  CHECK(self->PushOnThreadLocalAllocationStack(*obj));  // Must succeed.
+  CHECK(self->PushOnThreadLocalAllocationStack(obj->Ptr()));  // Must succeed.
 }
 
 // Must do this with mutators suspended since we are directly accessing the allocation stacks.
@@ -3737,7 +3695,7 @@
   }
 }
 
-void Heap::AddFinalizerReference(Thread* self, mirror::Object** object) {
+void Heap::AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object) {
   ScopedObjectAccess soa(self);
   ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(*object));
   jvalue args[1];
@@ -3747,9 +3705,11 @@
   *object = soa.Decode<mirror::Object>(arg.get()).Ptr();
 }
 
-void Heap::RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj) {
+void Heap::RequestConcurrentGCAndSaveObject(Thread* self,
+                                            bool force_full,
+                                            ObjPtr<mirror::Object>* obj) {
   StackHandleScope<1> hs(self);
-  HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
+  HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
   RequestConcurrentGC(self, force_full);
 }
 
@@ -4026,7 +3986,7 @@
   mod_union_tables_.Put(mod_union_table->GetSpace(), mod_union_table);
 }
 
-void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) {
+void Heap::CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count) {
   CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
         (c->IsVariableSize() || c->GetObjectSize() == byte_count)) << c->GetClassFlags();
   CHECK_GE(byte_count, sizeof(mirror::Object));
@@ -4152,7 +4112,7 @@
   return state.GetFrameCount();
 }
 
-void Heap::CheckGcStressMode(Thread* self, mirror::Object** obj) {
+void Heap::CheckGcStressMode(Thread* self, ObjPtr<mirror::Object>* obj) {
   auto* const runtime = Runtime::Current();
   if (gc_stress_mode_ && runtime->GetClassLinker()->IsInitialized() &&
       !runtime->IsActiveTransaction() && mirror::Class::HasJavaLangClass()) {
@@ -4191,9 +4151,9 @@
   gc_disabled_for_shutdown_ = true;
 }
 
-bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const {
+bool Heap::ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const {
   for (gc::space::ImageSpace* space : boot_image_spaces_) {
-    if (space->HasAddress(obj)) {
+    if (space->HasAddress(obj.Ptr())) {
       return true;
     }
   }
@@ -4250,7 +4210,7 @@
   AllocationListener* old = GetAndOverwriteAllocationListener(&alloc_listener_, nullptr);
 
   if (old != nullptr) {
-    Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
+    Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
   }
 }
 
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 5e17a52..6d37140 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -34,6 +34,7 @@
 #include "gc/collector_type.h"
 #include "gc/space/large_object_space.h"
 #include "globals.h"
+#include "handle.h"
 #include "obj_ptr.h"
 #include "object_callbacks.h"
 #include "offsets.h"
@@ -49,6 +50,7 @@
 class Thread;
 class ThreadPool;
 class TimingLogger;
+class VariableSizedHandleScope;
 
 namespace mirror {
   class Class;
@@ -194,36 +196,48 @@
   // Allocates and initializes storage for an object instance.
   template <bool kInstrumented, typename PreFenceVisitor>
   mirror::Object* AllocObject(Thread* self,
-                              mirror::Class* klass,
+                              ObjPtr<mirror::Class> klass,
                               size_t num_bytes,
                               const PreFenceVisitor& pre_fence_visitor)
       REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_,
+      REQUIRES(!*gc_complete_lock_,
+               !*pending_task_lock_,
+               !*backtrace_lock_,
                !Roles::uninterruptible_) {
-    return AllocObjectWithAllocator<kInstrumented, true>(
-        self, klass, num_bytes, GetCurrentAllocator(), pre_fence_visitor);
+    return AllocObjectWithAllocator<kInstrumented, true>(self,
+                                                         klass,
+                                                         num_bytes,
+                                                         GetCurrentAllocator(),
+                                                         pre_fence_visitor);
   }
 
   template <bool kInstrumented, typename PreFenceVisitor>
   mirror::Object* AllocNonMovableObject(Thread* self,
-                                        mirror::Class* klass,
+                                        ObjPtr<mirror::Class> klass,
                                         size_t num_bytes,
                                         const PreFenceVisitor& pre_fence_visitor)
       REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_,
+      REQUIRES(!*gc_complete_lock_,
+               !*pending_task_lock_,
+               !*backtrace_lock_,
                !Roles::uninterruptible_) {
-    return AllocObjectWithAllocator<kInstrumented, true>(
-        self, klass, num_bytes, GetCurrentNonMovingAllocator(), pre_fence_visitor);
+    return AllocObjectWithAllocator<kInstrumented, true>(self,
+                                                         klass,
+                                                         num_bytes,
+                                                         GetCurrentNonMovingAllocator(),
+                                                         pre_fence_visitor);
   }
 
   template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
   ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(Thread* self,
-                                                         mirror::Class* klass,
+                                                         ObjPtr<mirror::Class> klass,
                                                          size_t byte_count,
                                                          AllocatorType allocator,
                                                          const PreFenceVisitor& pre_fence_visitor)
       REQUIRES_SHARED(Locks::mutator_lock_)
-      REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_,
+      REQUIRES(!*gc_complete_lock_,
+               !*pending_task_lock_,
+               !*backtrace_lock_,
                !Roles::uninterruptible_);
 
   AllocatorType GetCurrentAllocator() const {
@@ -241,7 +255,7 @@
   void VisitObjectsPaused(ObjectCallback callback, void* arg)
       REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_);
 
-  void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count)
+  void CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void RegisterNativeAllocation(JNIEnv* env, size_t bytes)
@@ -263,7 +277,7 @@
   // The given reference is believed to be to an object in the Java heap, check the soundness of it.
   // TODO: NO_THREAD_SAFETY_ANALYSIS since we call this everywhere and it is impossible to find a
   // proper lock ordering for it.
-  void VerifyObjectBody(mirror::Object* o) NO_THREAD_SAFETY_ANALYSIS;
+  void VerifyObjectBody(ObjPtr<mirror::Object> o) NO_THREAD_SAFETY_ANALYSIS;
 
   // Check sanity of all live references.
   void VerifyHeap() REQUIRES(!Locks::heap_bitmap_lock_);
@@ -276,16 +290,16 @@
   // A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock,
   // and doesn't abort on error, allowing the caller to report more
   // meaningful diagnostics.
-  bool IsValidObjectAddress(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsValidObjectAddress(const void* obj) const REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Faster alternative to IsHeapAddress since finding if an object is in the large object space is
   // very slow.
-  bool IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const
+  bool IsNonDiscontinuousSpaceHeapAddress(const void* addr) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if 'obj' is a live heap object, false otherwise (including for invalid addresses).
   // Requires the heap lock to be held.
-  bool IsLiveObjectLocked(mirror::Object* obj,
+  bool IsLiveObjectLocked(ObjPtr<mirror::Object> obj,
                           bool search_allocation_stack = true,
                           bool search_live_stack = true,
                           bool sorted = false)
@@ -321,19 +335,25 @@
 
   // Implements VMDebug.countInstancesOfClass and JDWP VM_InstanceCount.
   // The boolean decides whether to use IsAssignableFrom or == when comparing classes.
-  void CountInstances(const std::vector<mirror::Class*>& classes,
+  void CountInstances(const std::vector<Handle<mirror::Class>>& classes,
                       bool use_is_assignable_from,
                       uint64_t* counts)
       REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Implements JDWP RT_Instances.
-  void GetInstances(mirror::Class* c, int32_t max_count, std::vector<mirror::Object*>& instances)
+  void GetInstances(VariableSizedHandleScope& scope,
+                    Handle<mirror::Class> c,
+                    int32_t max_count,
+                    std::vector<Handle<mirror::Object>>& instances)
       REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Implements JDWP OR_ReferringObjects.
-  void GetReferringObjects(mirror::Object* o,
+  void GetReferringObjects(VariableSizedHandleScope& scope,
+                           Handle<mirror::Object> o,
                            int32_t max_count,
-                           std::vector<mirror::Object*>& referring_objects)
+                           std::vector<Handle<mirror::Object>>& referring_objects)
       REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -445,16 +465,14 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Write barrier for array operations that update many field positions
-  ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst,
-                                       int start_offset ATTRIBUTE_UNUSED,
+  ALWAYS_INLINE void WriteBarrierArray(ObjPtr<mirror::Object> dst,
+                                       int start_offset,
                                        // TODO: element_count or byte_count?
-                                       size_t length ATTRIBUTE_UNUSED) {
-    card_table_->MarkCard(dst);
-  }
+                                       size_t length)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
-  ALWAYS_INLINE void WriteBarrierEveryFieldOf(const mirror::Object* obj) {
-    card_table_->MarkCard(obj);
-  }
+  ALWAYS_INLINE void WriteBarrierEveryFieldOf(ObjPtr<mirror::Object> obj)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   accounting::CardTable* GetCardTable() const {
     return card_table_.get();
@@ -464,7 +482,7 @@
     return rb_table_.get();
   }
 
-  void AddFinalizerReference(Thread* self, mirror::Object** object);
+  void AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object);
 
   // Returns the number of bytes currently allocated.
   size_t GetBytesAllocated() const {
@@ -527,12 +545,20 @@
   // get the space that corresponds to an object's address. Current implementation searches all
   // spaces in turn. If fail_ok is false then failing to find a space will cause an abort.
   // TODO: consider using faster data structure like binary tree.
-  space::ContinuousSpace* FindContinuousSpaceFromObject(const mirror::Object*, bool fail_ok) const
+  space::ContinuousSpace* FindContinuousSpaceFromObject(ObjPtr<mirror::Object>, bool fail_ok) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(const mirror::Object*,
+
+  space::ContinuousSpace* FindContinuousSpaceFromAddress(const mirror::Object* addr) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(ObjPtr<mirror::Object>,
                                                               bool fail_ok) const
       REQUIRES_SHARED(Locks::mutator_lock_);
-  space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const
+
+  space::Space* FindSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  space::Space* FindSpaceFromAddress(const void* ptr) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void DumpForSigQuit(std::ostream& os) REQUIRES(!*gc_complete_lock_, !native_histogram_lock_);
@@ -598,7 +624,7 @@
     return boot_image_spaces_;
   }
 
-  bool ObjectIsInBootImageSpace(mirror::Object* obj) const
+  bool ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   bool IsInBootImageOatFile(const void* p) const
@@ -650,12 +676,6 @@
   void DumpSpaces(std::ostream& stream) const REQUIRES_SHARED(Locks::mutator_lock_);
   std::string DumpSpaces() const REQUIRES_SHARED(Locks::mutator_lock_);
 
-  // Dump object should only be used by the signal handler.
-  void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
-  // Safe version of pretty type of which check to make sure objects are heap addresses.
-  std::string SafeGetClassDescriptor(mirror::Class* klass) NO_THREAD_SAFETY_ANALYSIS;
-  std::string SafePrettyTypeOf(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
-
   // GC performance measuring
   void DumpGcPerformanceInfo(std::ostream& os)
       REQUIRES(!*gc_complete_lock_, !native_histogram_lock_);
@@ -837,11 +857,11 @@
         collector_type == kCollectorTypeMC ||
         collector_type == kCollectorTypeHomogeneousSpaceCompact;
   }
-  bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const
+  bool ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const
       REQUIRES_SHARED(Locks::mutator_lock_);
   ALWAYS_INLINE void CheckConcurrentGC(Thread* self,
                                        size_t new_num_bytes_allocated,
-                                       mirror::Object** obj)
+                                       ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
 
@@ -852,7 +872,7 @@
   // We don't force this to be inlined since it is a slow path.
   template <bool kInstrumented, typename PreFenceVisitor>
   mirror::Object* AllocLargeObject(Thread* self,
-                                   mirror::Class** klass,
+                                   ObjPtr<mirror::Class>* klass,
                                    size_t byte_count,
                                    const PreFenceVisitor& pre_fence_visitor)
       REQUIRES_SHARED(Locks::mutator_lock_)
@@ -867,14 +887,14 @@
                                          size_t* bytes_allocated,
                                          size_t* usable_size,
                                          size_t* bytes_tl_bulk_allocated,
-                                         mirror::Class** klass)
+                                         ObjPtr<mirror::Class>* klass)
       REQUIRES(!Locks::thread_suspend_count_lock_, !*gc_complete_lock_, !*pending_task_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Allocate into a specific space.
   mirror::Object* AllocateInto(Thread* self,
                                space::AllocSpace* space,
-                               mirror::Class* c,
+                               ObjPtr<mirror::Class> c,
                                size_t bytes)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -899,10 +919,6 @@
   template <bool kGrow>
   ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
 
-  // Returns true if the address passed in is within the address range of a continuous space.
-  bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Run the finalizers. If timeout is non zero, then we use the VMRuntime version.
   void RunFinalization(JNIEnv* env, uint64_t timeout);
 
@@ -914,7 +930,7 @@
   void RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time)
       REQUIRES(!*pending_task_lock_);
 
-  void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj)
+  void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*pending_task_lock_);
   bool IsGCRequestPending() const;
@@ -986,13 +1002,13 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Push an object onto the allocation stack.
-  void PushOnAllocationStack(Thread* self, mirror::Object** obj)
+  void PushOnAllocationStack(Thread* self, ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
-  void PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj)
+  void PushOnAllocationStackWithInternalGC(Thread* self, ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
-  void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, mirror::Object** obj)
+  void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
 
@@ -1023,7 +1039,7 @@
   void UpdateGcCountRateHistograms() REQUIRES(gc_complete_lock_);
 
   // GC stress mode attempts to do one GC per unique backtrace.
-  void CheckGcStressMode(Thread* self, mirror::Object** obj)
+  void CheckGcStressMode(Thread* self, ObjPtr<mirror::Object>* obj)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_);
 
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index cceb007..b212d09 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -28,24 +28,30 @@
 namespace art {
 
 template<size_t kNumReferences>
-inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
-    : HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) {
-  DCHECK_EQ(self, Thread::Current());
+inline FixedSizeHandleScope<kNumReferences>::FixedSizeHandleScope(BaseHandleScope* link,
+                                                                  mirror::Object* fill_value)
+    : HandleScope(link, kNumReferences) {
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference");
-  // TODO: Figure out how to use a compile assert.
-  CHECK_EQ(&storage_[0], GetReferences());
+  static_assert(kNumReferences >= 1, "FixedSizeHandleScope must contain at least 1 reference");
+  DCHECK_EQ(&storage_[0], GetReferences());  // TODO: Figure out how to use a compile assert.
   for (size_t i = 0; i < kNumReferences; ++i) {
     SetReference(i, fill_value);
   }
+}
+
+template<size_t kNumReferences>
+inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
+    : FixedSizeHandleScope<kNumReferences>(self->GetTopHandleScope(), fill_value),
+      self_(self) {
+  DCHECK_EQ(self, Thread::Current());
   self_->PushHandleScope(this);
 }
 
 template<size_t kNumReferences>
 inline StackHandleScope<kNumReferences>::~StackHandleScope() {
-  HandleScope* top_handle_scope = self_->PopHandleScope();
+  BaseHandleScope* top_handle_scope = self_->PopHandleScope();
   DCHECK_EQ(top_handle_scope, this);
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(self_);
@@ -66,7 +72,7 @@
 }
 
 inline mirror::Object* HandleScope::GetReference(size_t i) const {
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
@@ -74,12 +80,12 @@
 }
 
 inline Handle<mirror::Object> HandleScope::GetHandle(size_t i) {
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   return Handle<mirror::Object>(&GetReferences()[i]);
 }
 
 inline MutableHandle<mirror::Object> HandleScope::GetMutableHandle(size_t i) {
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   return MutableHandle<mirror::Object>(&GetReferences()[i]);
 }
 
@@ -87,7 +93,7 @@
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
-  DCHECK_LT(i, number_of_references_);
+  DCHECK_LT(i, NumberOfReferences());
   GetReferences()[i].Assign(object);
 }
 
@@ -95,13 +101,13 @@
   // A HandleScope should always contain something. One created by the
   // jni_compiler should have a jobject/jclass as a native method is
   // passed in a this pointer or a class
-  DCHECK_GT(number_of_references_, 0U);
+  DCHECK_GT(NumberOfReferences(), 0U);
   return &GetReferences()[0] <= handle_scope_entry &&
       handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
 }
 
 template<size_t kNumReferences> template<class T>
-inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) {
+inline MutableHandle<T> FixedSizeHandleScope<kNumReferences>::NewHandle(T* object) {
   SetReference(pos_, object);
   MutableHandle<T> h(GetHandle<T>(pos_));
   pos_++;
@@ -109,24 +115,24 @@
 }
 
 template<size_t kNumReferences> template<class MirrorType, bool kPoison>
-inline MutableHandle<MirrorType> StackHandleScope<kNumReferences>::NewHandle(
+inline MutableHandle<MirrorType> FixedSizeHandleScope<kNumReferences>::NewHandle(
     ObjPtr<MirrorType, kPoison> object) {
   return NewHandle(object.Ptr());
 }
 
 template<size_t kNumReferences> template<class T>
-inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
+inline HandleWrapper<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
   return HandleWrapper<T>(object, NewHandle(*object));
 }
 
 template<size_t kNumReferences> template<class T>
-inline HandleWrapperObjPtr<T> StackHandleScope<kNumReferences>::NewHandleWrapper(
+inline HandleWrapperObjPtr<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(
     ObjPtr<T>* object) {
   return HandleWrapperObjPtr<T>(object, NewHandle(*object));
 }
 
 template<size_t kNumReferences>
-inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
+inline void FixedSizeHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
   if (kDebugLocking) {
     Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
   }
@@ -135,12 +141,111 @@
   GetReferences()[i].Assign(object);
 }
 
+// Number of references contained within this handle scope.
+inline uint32_t BaseHandleScope::NumberOfReferences() const {
+  return LIKELY(!IsVariableSized())
+      ? AsHandleScope()->NumberOfReferences()
+      : AsVariableSized()->NumberOfReferences();
+}
+
+inline bool BaseHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) const {
+  return LIKELY(!IsVariableSized())
+      ? AsHandleScope()->Contains(handle_scope_entry)
+      : AsVariableSized()->Contains(handle_scope_entry);
+}
+
+template <typename Visitor>
+inline void BaseHandleScope::VisitRoots(Visitor& visitor) {
+  if (LIKELY(!IsVariableSized())) {
+    AsHandleScope()->VisitRoots(visitor);
+  } else {
+    AsVariableSized()->VisitRoots(visitor);
+  }
+}
+
+inline VariableSizedHandleScope* BaseHandleScope::AsVariableSized() {
+  DCHECK(IsVariableSized());
+  return down_cast<VariableSizedHandleScope*>(this);
+}
+
+inline HandleScope* BaseHandleScope::AsHandleScope() {
+  DCHECK(!IsVariableSized());
+  return down_cast<HandleScope*>(this);
+}
+
+inline const VariableSizedHandleScope* BaseHandleScope::AsVariableSized() const {
+  DCHECK(IsVariableSized());
+  return down_cast<const VariableSizedHandleScope*>(this);
+}
+
+inline const HandleScope* BaseHandleScope::AsHandleScope() const {
+  DCHECK(!IsVariableSized());
+  return down_cast<const HandleScope*>(this);
+}
+
+template<class T>
+MutableHandle<T> VariableSizedHandleScope::NewHandle(T* object) {
+  if (current_scope_->RemainingSlots() == 0) {
+    current_scope_ = new LocalScopeType(current_scope_);
+  }
+  return current_scope_->NewHandle(object);
+}
+
 template<class MirrorType, bool kPoison>
-inline MutableHandle<MirrorType> StackHandleScopeCollection::NewHandle(
+inline MutableHandle<MirrorType> VariableSizedHandleScope::NewHandle(
     ObjPtr<MirrorType, kPoison> ptr) {
   return NewHandle(ptr.Ptr());
 }
 
+inline VariableSizedHandleScope::VariableSizedHandleScope(Thread* const self)
+    : BaseHandleScope(self->GetTopHandleScope()),
+      self_(self) {
+  current_scope_ = new LocalScopeType(/*link*/ nullptr);
+  self_->PushHandleScope(this);
+}
+
+inline VariableSizedHandleScope::~VariableSizedHandleScope() {
+  BaseHandleScope* top_handle_scope = self_->PopHandleScope();
+  DCHECK_EQ(top_handle_scope, this);
+  while (current_scope_ != nullptr) {
+    LocalScopeType* next = reinterpret_cast<LocalScopeType*>(current_scope_->GetLink());
+    delete current_scope_;
+    current_scope_ = next;
+  }
+}
+
+inline uint32_t VariableSizedHandleScope::NumberOfReferences() const {
+  uint32_t sum = 0;
+  const LocalScopeType* cur = current_scope_;
+  while (cur != nullptr) {
+    sum += cur->NumberOfReferences();
+    cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink());
+  }
+  return sum;
+}
+
+inline bool VariableSizedHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry)
+    const {
+  const LocalScopeType* cur = current_scope_;
+  while (cur != nullptr) {
+    if (cur->Contains(handle_scope_entry)) {
+      return true;
+    }
+    cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink());
+  }
+  return false;
+}
+
+template <typename Visitor>
+inline void VariableSizedHandleScope::VisitRoots(Visitor& visitor) {
+  LocalScopeType* cur = current_scope_;
+  while (cur != nullptr) {
+    cur->VisitRoots(visitor);
+    cur = reinterpret_cast<LocalScopeType*>(cur->GetLink());
+  }
+}
+
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_HANDLE_SCOPE_INL_H_
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index fc729a5..8a0aba6 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -29,26 +29,69 @@
 
 namespace art {
 
+class HandleScope;
 template<class MirrorType, bool kPoison> class ObjPtr;
+class Thread;
+class VariableSizedHandleScope;
 
 namespace mirror {
 class Object;
 }
 
-class Thread;
+// Basic handle scope, tracked by a list. May be variable sized.
+class PACKED(4) BaseHandleScope {
+ public:
+  bool IsVariableSized() const {
+    return number_of_references_ == kNumReferencesVariableSized;
+  }
+
+  // Number of references contained within this handle scope.
+  ALWAYS_INLINE uint32_t NumberOfReferences() const;
+
+  ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const;
+
+  template <typename Visitor>
+  ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+  // Link to previous BaseHandleScope or null.
+  BaseHandleScope* GetLink() const {
+    return link_;
+  }
+
+  ALWAYS_INLINE VariableSizedHandleScope* AsVariableSized();
+  ALWAYS_INLINE HandleScope* AsHandleScope();
+  ALWAYS_INLINE const VariableSizedHandleScope* AsVariableSized() const;
+  ALWAYS_INLINE const HandleScope* AsHandleScope() const;
+
+ protected:
+  BaseHandleScope(BaseHandleScope* link, uint32_t num_references)
+      : link_(link),
+        number_of_references_(num_references) {}
+
+  // Variable sized constructor.
+  BaseHandleScope(BaseHandleScope* link)
+      : link_(link),
+        number_of_references_(kNumReferencesVariableSized) {}
+
+  static constexpr int32_t kNumReferencesVariableSized = -1;
+
+  // Link-list of handle scopes. The root is held by a Thread.
+  BaseHandleScope* const link_;
+
+  // Number of handlerized references. -1 for variable sized handle scopes.
+  const int32_t number_of_references_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BaseHandleScope);
+};
 
 // HandleScopes are scoped objects containing a number of Handles. They are used to allocate
 // handles, for these handles (and the objects contained within them) to be visible/roots for the
 // GC. It is most common to stack allocate HandleScopes using StackHandleScope.
-class PACKED(4) HandleScope {
+class PACKED(4) HandleScope : public BaseHandleScope {
  public:
   ~HandleScope() {}
 
-  // Number of references contained within this handle scope.
-  uint32_t NumberOfReferences() const {
-    return number_of_references_;
-  }
-
   // We have versions with and without explicit pointer size of the following. The first two are
   // used at runtime, so OFFSETOF_MEMBER computes the right offsets automatically. The last one
   // takes the pointer size explicitly so that at compile time we can cross-compile correctly.
@@ -59,11 +102,6 @@
   // Returns the size of a HandleScope containing num_references handles.
   static size_t SizeOf(PointerSize pointer_size, uint32_t num_references);
 
-  // Link to previous HandleScope or null.
-  HandleScope* GetLink() const {
-    return link_;
-  }
-
   ALWAYS_INLINE mirror::Object* GetReference(size_t i) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -93,11 +131,26 @@
   }
 
   // Placement new creation.
-  static HandleScope* Create(void* storage, HandleScope* link, uint32_t num_references)
+  static HandleScope* Create(void* storage, BaseHandleScope* link, uint32_t num_references)
       WARN_UNUSED {
     return new (storage) HandleScope(link, num_references);
   }
 
+  // Number of references contained within this handle scope.
+  ALWAYS_INLINE uint32_t NumberOfReferences() const {
+    DCHECK_GE(number_of_references_, 0);
+    return static_cast<uint32_t>(number_of_references_);
+  }
+
+  template <typename Visitor>
+  void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
+    for (size_t i = 0, count = NumberOfReferences(); i < count; ++i) {
+      // GetReference returns a pointer to the stack reference within the handle scope. If this
+      // needs to be updated, it will be done by the root visitor.
+      visitor.VisitRootIfNonNull(GetHandle(i).GetReference());
+    }
+  }
+
  protected:
   // Return backing storage used for references.
   ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const {
@@ -105,20 +158,11 @@
     return reinterpret_cast<StackReference<mirror::Object>*>(address);
   }
 
-  explicit HandleScope(size_t number_of_references) :
-      link_(nullptr), number_of_references_(number_of_references) {
-  }
+  explicit HandleScope(size_t number_of_references) : HandleScope(nullptr, number_of_references) {}
 
   // Semi-hidden constructor. Construction expected by generated code and StackHandleScope.
-  HandleScope(HandleScope* link, uint32_t num_references) :
-      link_(link), number_of_references_(num_references) {
-  }
-
-  // Link-list of handle scopes. The root is held by a Thread.
-  HandleScope* const link_;
-
-  // Number of handlerized references.
-  const uint32_t number_of_references_;
+  HandleScope(BaseHandleScope* link, uint32_t num_references)
+      : BaseHandleScope(link, num_references) {}
 
   // Storage for references.
   // StackReference<mirror::Object> references_[number_of_references_]
@@ -165,14 +209,10 @@
   ObjPtr<T>* const obj_;
 };
 
-
-// Scoped handle storage of a fixed size that is usually stack allocated.
+// Fixed size handle scope that is not necessarily linked in the thread.
 template<size_t kNumReferences>
-class PACKED(4) StackHandleScope FINAL : public HandleScope {
+class PACKED(4) FixedSizeHandleScope : public HandleScope {
  public:
-  explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr);
-  ALWAYS_INLINE ~StackHandleScope();
-
   template<class T>
   ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -191,11 +231,15 @@
   ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  Thread* Self() const {
-    return self_;
+  size_t RemainingSlots() const {
+    return kNumReferences - pos_;
   }
 
  private:
+  explicit ALWAYS_INLINE FixedSizeHandleScope(BaseHandleScope* link,
+                                              mirror::Object* fill_value = nullptr);
+  ALWAYS_INLINE ~FixedSizeHandleScope() {}
+
   template<class T>
   ALWAYS_INLINE MutableHandle<T> GetHandle(size_t i) REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK_LT(i, kNumReferences);
@@ -205,66 +249,65 @@
   // Reference storage needs to be first as expected by the HandleScope layout.
   StackReference<mirror::Object> storage_[kNumReferences];
 
+  // Position new handles will be created.
+  size_t pos_ = 0;
+
+  template<size_t kNumRefs> friend class StackHandleScope;
+  friend class VariableSizedHandleScope;
+};
+
+// Scoped handle storage of a fixed size that is stack allocated.
+template<size_t kNumReferences>
+class PACKED(4) StackHandleScope FINAL : public FixedSizeHandleScope<kNumReferences> {
+ public:
+  explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr);
+  ALWAYS_INLINE ~StackHandleScope();
+
+  Thread* Self() const {
+    return self_;
+  }
+
+ private:
   // The thread that the stack handle scope is a linked list upon. The stack handle scope will
   // push and pop itself from this thread.
   Thread* const self_;
-
-  // Position new handles will be created.
-  size_t pos_;
-
-  template<size_t kNumRefs> friend class StackHandleScope;
 };
 
-// Utility class to manage a collection (stack) of StackHandleScope. All the managed
-// scope handle have the same fixed sized.
-// Calls to NewHandle will create a new handle inside the top StackHandleScope.
-// When the handle scope becomes full a new one is created and push on top of the
-// previous.
-//
-// NB:
-// - it is not safe to use the *same* StackHandleScopeCollection intermix with
-// other StackHandleScopes.
-// - this is a an easy way around implementing a full ZoneHandleScope to manage an
-// arbitrary number of handles.
-class StackHandleScopeCollection {
+// Utility class to manage a variable sized handle scope by having a list of fixed size handle
+// scopes.
+// Calls to NewHandle will create a new handle inside the current FixedSizeHandleScope.
+// When the current handle scope becomes full a new one is created and put at the front of the
+// list.
+class VariableSizedHandleScope : public BaseHandleScope {
  public:
-  explicit StackHandleScopeCollection(Thread* const self) :
-      self_(self),
-      current_scope_num_refs_(0) {
-  }
-
-  ~StackHandleScopeCollection() {
-    while (!scopes_.empty()) {
-      delete scopes_.top();
-      scopes_.pop();
-    }
-  }
+  explicit VariableSizedHandleScope(Thread* const self);
+  ~VariableSizedHandleScope();
 
   template<class T>
-  MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (scopes_.empty() || current_scope_num_refs_ >= kNumReferencesPerScope) {
-      StackHandleScope<kNumReferencesPerScope>* scope =
-          new StackHandleScope<kNumReferencesPerScope>(self_);
-      scopes_.push(scope);
-      current_scope_num_refs_ = 0;
-    }
-    current_scope_num_refs_++;
-    return scopes_.top()->NewHandle(object);
-  }
+  MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_);
 
   template<class MirrorType, bool kPoison>
   MutableHandle<MirrorType> NewHandle(ObjPtr<MirrorType, kPoison> ptr)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Number of references contained within this handle scope.
+  ALWAYS_INLINE uint32_t NumberOfReferences() const;
+
+  ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const;
+
+  template <typename Visitor>
+  void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   static constexpr size_t kNumReferencesPerScope = 4;
 
   Thread* const self_;
 
-  std::stack<StackHandleScope<kNumReferencesPerScope>*> scopes_;
-  size_t current_scope_num_refs_;
+  // Linked list of fixed size handle scopes.
+  using LocalScopeType = FixedSizeHandleScope<kNumReferencesPerScope>;
+  LocalScopeType* current_scope_;
 
-  DISALLOW_COPY_AND_ASSIGN(StackHandleScopeCollection);
+  DISALLOW_COPY_AND_ASSIGN(VariableSizedHandleScope);
 };
 
 }  // namespace art
diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc
index c269a37..92063c4 100644
--- a/runtime/handle_scope_test.cc
+++ b/runtime/handle_scope_test.cc
@@ -15,6 +15,7 @@
  */
 
 #include "base/enums.h"
+#include "common_runtime_test.h"
 #include "gtest/gtest.h"
 #include "handle_scope-inl.h"
 #include "scoped_thread_state_change-inl.h"
@@ -22,51 +23,85 @@
 
 namespace art {
 
-// Handle scope with a fixed size which is allocated on the stack.
-template<size_t kNumReferences>
-class NoThreadStackHandleScope : public HandleScope {
- public:
-  explicit NoThreadStackHandleScope(HandleScope* link) : HandleScope(link, kNumReferences) {
-  }
-  ~NoThreadStackHandleScope() {
-  }
-
- private:
-  // references_storage_ needs to be first so that it matches the address of references_
-  StackReference<mirror::Object> references_storage_[kNumReferences];
-};
+class HandleScopeTest : public CommonRuntimeTest {};
 
 // Test the offsets computed for members of HandleScope. Because of cross-compiling
 // it is impossible the use OFFSETOF_MEMBER, so we do some reasonable computations ourselves. This
 // test checks whether we do the right thing.
-TEST(HandleScopeTest, Offsets) NO_THREAD_SAFETY_ANALYSIS {
+TEST_F(HandleScopeTest, Offsets) {
+  ScopedObjectAccess soa(Thread::Current());
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
   // As the members of HandleScope are private, we cannot use OFFSETOF_MEMBER
   // here. So do the inverse: set some data, and access it through pointers created from the offsets.
-  NoThreadStackHandleScope<0x9ABC> test_table(reinterpret_cast<HandleScope*>(0x5678));
-  test_table.SetReference(0, reinterpret_cast<mirror::Object*>(0x1234));
+  StackHandleScope<0x1> hs0(soa.Self());
+  static const size_t kNumReferences = 0x9ABC;
+  StackHandleScope<kNumReferences> test_table(soa.Self());
+  ObjPtr<mirror::Class> c = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+  test_table.SetReference(0, c.Ptr());
 
   uint8_t* table_base_ptr = reinterpret_cast<uint8_t*>(&test_table);
 
   {
-    uintptr_t* link_ptr = reinterpret_cast<uintptr_t*>(table_base_ptr +
+    BaseHandleScope** link_ptr = reinterpret_cast<BaseHandleScope**>(table_base_ptr +
         HandleScope::LinkOffset(kRuntimePointerSize));
-    EXPECT_EQ(*link_ptr, static_cast<size_t>(0x5678));
+    EXPECT_EQ(*link_ptr, &hs0);
   }
 
   {
     uint32_t* num_ptr = reinterpret_cast<uint32_t*>(table_base_ptr +
         HandleScope::NumberOfReferencesOffset(kRuntimePointerSize));
-    EXPECT_EQ(*num_ptr, static_cast<size_t>(0x9ABC));
+    EXPECT_EQ(*num_ptr, static_cast<size_t>(kNumReferences));
   }
 
   {
-    // Assume sizeof(StackReference<mirror::Object>) == sizeof(uint32_t)
-    // TODO: How can we make this assumption-less but still access directly and fully?
-    EXPECT_EQ(sizeof(StackReference<mirror::Object>), sizeof(uint32_t));
-
-    uint32_t* ref_ptr = reinterpret_cast<uint32_t*>(table_base_ptr +
+    auto* ref_ptr = reinterpret_cast<StackReference<mirror::Object>*>(table_base_ptr +
         HandleScope::ReferencesOffset(kRuntimePointerSize));
-    EXPECT_EQ(*ref_ptr, static_cast<uint32_t>(0x1234));
+    EXPECT_OBJ_PTR_EQ(ref_ptr->AsMirrorPtr(), c);
+  }
+}
+
+class CollectVisitor {
+ public:
+  void VisitRootIfNonNull(StackReference<mirror::Object>* ref) {
+    if (!ref->IsNull()) {
+      visited.insert(ref);
+    }
+    ++total_visited;
+  }
+
+  std::set<StackReference<mirror::Object>*> visited;
+  size_t total_visited = 0;  // including null.
+};
+
+// Test functionality of variable sized handle scopes.
+TEST_F(HandleScopeTest, VariableSized) {
+  ScopedObjectAccess soa(Thread::Current());
+  VariableSizedHandleScope hs(soa.Self());
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  Handle<mirror::Class> c =
+      hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+  // Test nested scopes.
+  StackHandleScope<1> inner(soa.Self());
+  inner.NewHandle(c->AllocObject(soa.Self()));
+  // Add a bunch of handles and make sure callbacks work.
+  static const size_t kNumHandles = 100;
+  std::vector<Handle<mirror::Object>> handles;
+  for (size_t i = 0; i < kNumHandles; ++i) {
+    BaseHandleScope* base = &hs;
+    ObjPtr<mirror::Object> o = c->AllocObject(soa.Self());
+    handles.push_back(hs.NewHandle(o));
+    EXPECT_OBJ_PTR_EQ(o, handles.back().Get());
+    EXPECT_TRUE(hs.Contains(handles.back().GetReference()));
+    EXPECT_TRUE(base->Contains(handles.back().GetReference()));
+    EXPECT_EQ(hs.NumberOfReferences(), base->NumberOfReferences());
+  }
+  CollectVisitor visitor;
+  BaseHandleScope* base = &hs;
+  base->VisitRoots(visitor);
+  EXPECT_LE(visitor.visited.size(), base->NumberOfReferences());
+  EXPECT_EQ(visitor.total_visited, base->NumberOfReferences());
+  for (StackReference<mirror::Object>* ref : visitor.visited) {
+    EXPECT_TRUE(base->Contains(ref));
   }
 }
 
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index e05f8f3..e357fa6 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -90,7 +90,7 @@
   }
   uint32_t idx = ExtractIndex(iref);
   ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
-  VerifyObject(obj.Ptr());
+  VerifyObject(obj);
   return obj;
 }
 
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index d59bb39..6109ec6 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -99,7 +99,7 @@
   size_t topIndex = segment_state_.parts.topIndex;
 
   CHECK(obj != nullptr);
-  VerifyObject(obj.Ptr());
+  VerifyObject(obj);
   DCHECK(table_ != nullptr);
   DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
 
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f0a7c16..273c67d 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2481,7 +2481,7 @@
       // Sanity check: If elements is not the same as the java array's data, it better not be a
       // heap address. TODO: This might be slow to check, may be worth keeping track of which
       // copies we make?
-      if (heap->IsNonDiscontinuousSpaceHeapAddress(reinterpret_cast<mirror::Object*>(elements))) {
+      if (heap->IsNonDiscontinuousSpaceHeapAddress(elements)) {
         soa.Vm()->JniAbortF("ReleaseArrayElements",
                             "invalid element pointer %p, array elements are %p",
                             reinterpret_cast<void*>(elements), array_data);
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 9d7f98f..7cbcac8 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -100,10 +100,10 @@
   explicit SetLengthVisitor(int32_t length) : length_(length) {
   }
 
-  void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Avoid AsArray as object is not yet in live bitmap or allocation stack.
-    Array* array = down_cast<Array*>(obj);
+    ObjPtr<Array> array = ObjPtr<Array>::DownCast(obj);
     // DCHECK(array->IsArrayInstance());
     array->SetLength(length_);
   }
@@ -124,10 +124,10 @@
       component_size_shift_(component_size_shift) {
   }
 
-  void operator()(Object* obj, size_t usable_size) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Avoid AsArray as object is not yet in live bitmap or allocation stack.
-    Array* array = down_cast<Array*>(obj);
+    ObjPtr<Array> array = ObjPtr<Array>::DownCast(obj);
     // DCHECK(array->IsArrayInstance());
     int32_t length = (usable_size - header_size_) >> component_size_shift_;
     DCHECK_GE(length, minimum_length_);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index cc088b8..98d383d 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -707,9 +707,13 @@
   if (!kCheckAddFinalizer) {
     DCHECK(!IsFinalizable());
   }
-  mirror::Object* obj =
-      heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, this, this->object_size_,
-                                                             allocator_type, VoidFunctor());
+  // Note that the this pointer may be invalidated after the allocation.
+  ObjPtr<mirror::Object> obj =
+      heap->AllocObjectWithAllocator<kIsInstrumented, false>(self,
+                                                             this,
+                                                             this->object_size_,
+                                                             allocator_type,
+                                                             VoidFunctor());
   if (add_finalizer && LIKELY(obj != nullptr)) {
     heap->AddFinalizerReference(self, &obj);
     if (UNLIKELY(self->IsExceptionPending())) {
@@ -717,7 +721,7 @@
       obj = nullptr;
     }
   }
-  return obj;
+  return obj.Ptr();
 }
 
 inline Object* Class::AllocObject(Thread* self) {
@@ -879,11 +883,11 @@
   SetFieldBoolean<false, false>(GetSlowPathFlagOffset(), enabled);
 }
 
-inline void Class::InitializeClassVisitor::operator()(
-    mirror::Object* obj, size_t usable_size) const {
+inline void Class::InitializeClassVisitor::operator()(ObjPtr<mirror::Object> obj,
+                                                      size_t usable_size) const {
   DCHECK_LE(class_size_, usable_size);
   // Avoid AsClass as object is not yet in live bitmap or allocation stack.
-  mirror::Class* klass = down_cast<mirror::Class*>(obj);
+  ObjPtr<mirror::Class> klass = ObjPtr<mirror::Class>::DownCast(obj);
   // DCHECK(klass->IsClass());
   klass->SetClassSize(class_size_);
   klass->SetPrimitiveType(Primitive::kPrimNot);  // Default to not being primitive.
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 40742d2..689dd22 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -998,11 +998,11 @@
         copy_bytes_(copy_bytes), imt_(imt), pointer_size_(pointer_size) {
   }
 
-  void operator()(mirror::Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<mirror::Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     StackHandleScope<1> hs(self_);
     Handle<mirror::Class> h_new_class_obj(hs.NewHandle(obj->AsClass()));
-    mirror::Object::CopyObject(self_, h_new_class_obj.Get(), orig_->Get(), copy_bytes_);
+    mirror::Object::CopyObject(h_new_class_obj.Get(), orig_->Get(), copy_bytes_);
     mirror::Class::SetStatus(h_new_class_obj, Class::kStatusResolving, self_);
     h_new_class_obj->PopulateEmbeddedVTable(pointer_size_);
     h_new_class_obj->SetImt(imt_, pointer_size_);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index a0d6f37..725939a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1232,7 +1232,7 @@
     explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) {
     }
 
-    void operator()(mirror::Object* obj, size_t usable_size) const
+    void operator()(ObjPtr<mirror::Object> obj, size_t usable_size) const
         REQUIRES_SHARED(Locks::mutator_lock_);
 
    private:
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 9d3c26e..daee727 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -43,10 +43,10 @@
 
 class CopyReferenceFieldsWithReadBarrierVisitor {
  public:
-  explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj)
+  explicit CopyReferenceFieldsWithReadBarrierVisitor(ObjPtr<Object> dest_obj)
       : dest_obj_(dest_obj) {}
 
-  void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const
+  void operator()(ObjPtr<Object> obj, MemberOffset offset, bool /* is_static */) const
       ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     // GetFieldObject() contains a RB.
     Object* ref = obj->GetFieldObject<Object>(offset);
@@ -55,7 +55,7 @@
     dest_obj_->SetFieldObjectWithoutWriteBarrier<false, false>(offset, ref);
   }
 
-  void operator()(mirror::Class* klass, mirror::Reference* ref) const
+  void operator()(ObjPtr<mirror::Class> klass, mirror::Reference* ref) const
       ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
     // Copy java.lang.ref.Reference.referent which isn't visited in
     // Object::VisitReferences().
@@ -69,18 +69,17 @@
   void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
 
  private:
-  Object* const dest_obj_;
+  ObjPtr<Object> const dest_obj_;
 };
 
-Object* Object::CopyObject(Thread* self,
-                           mirror::Object* dest,
-                           mirror::Object* src,
+Object* Object::CopyObject(ObjPtr<mirror::Object> dest,
+                           ObjPtr<mirror::Object> src,
                            size_t num_bytes) {
   // Copy instance data.  Don't assume memcpy copies by words (b/32012820).
   {
     const size_t offset = sizeof(Object);
-    uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src) + offset;
-    uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest) + offset;
+    uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src.Ptr()) + offset;
+    uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest.Ptr()) + offset;
     num_bytes -= offset;
     DCHECK_ALIGNED(src_bytes, sizeof(uintptr_t));
     DCHECK_ALIGNED(dst_bytes, sizeof(uintptr_t));
@@ -128,26 +127,21 @@
   } else {
     heap->WriteBarrierEveryFieldOf(dest);
   }
-  if (c->IsFinalizable()) {
-    heap->AddFinalizerReference(self, &dest);
-  }
-  return dest;
+  return dest.Ptr();
 }
 
 // An allocation pre-fence visitor that copies the object.
 class CopyObjectVisitor {
  public:
-  CopyObjectVisitor(Thread* self, Handle<Object>* orig, size_t num_bytes)
-      : self_(self), orig_(orig), num_bytes_(num_bytes) {
-  }
+  CopyObjectVisitor(Handle<Object>* orig, size_t num_bytes)
+      : orig_(orig), num_bytes_(num_bytes) {}
 
-  void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    Object::CopyObject(self_, obj, orig_->Get(), num_bytes_);
+    Object::CopyObject(obj, orig_->Get(), num_bytes_);
   }
 
  private:
-  Thread* const self_;
   Handle<Object>* const orig_;
   const size_t num_bytes_;
   DISALLOW_COPY_AND_ASSIGN(CopyObjectVisitor);
@@ -161,14 +155,17 @@
   size_t num_bytes = SizeOf();
   StackHandleScope<1> hs(self);
   Handle<Object> this_object(hs.NewHandle(this));
-  Object* copy;
-  CopyObjectVisitor visitor(self, &this_object, num_bytes);
+  ObjPtr<Object> copy;
+  CopyObjectVisitor visitor(&this_object, num_bytes);
   if (heap->IsMovableObject(this)) {
     copy = heap->AllocObject<true>(self, GetClass(), num_bytes, visitor);
   } else {
     copy = heap->AllocNonMovableObject<true>(self, GetClass(), num_bytes, visitor);
   }
-  return copy;
+  if (this_object->GetClass()->IsFinalizable()) {
+    heap->AddFinalizerReference(self, &copy);
+  }
+  return copy.Ptr();
 }
 
 uint32_t Object::GenerateIdentityHashCode() {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 9ddf995..84aa96c 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -609,10 +609,11 @@
     }
   }
 
-  // A utility function that copies an object in a read barrier and
-  // write barrier-aware way. This is internally used by Clone() and
-  // Class::CopyOf().
-  static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src,
+  // A utility function that copies an object in a read barrier and write barrier-aware way.
+  // This is internally used by Clone() and Class::CopyOf(). If the object is finalizable,
+  // it is the callers job to call Heap::AddFinalizerReference.
+  static Object* CopyObject(ObjPtr<mirror::Object> dest,
+                            ObjPtr<mirror::Object> src,
                             size_t num_bytes)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index aea6ff1..cf902af 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -43,10 +43,10 @@
   explicit SetStringCountVisitor(int32_t count) : count_(count) {
   }
 
-  void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Avoid AsString as object is not yet in live bitmap or allocation stack.
-    String* string = down_cast<String*>(obj);
+    ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
     string->SetCount(count_);
     DCHECK(!string->IsCompressed() || kUseStringCompression);
   }
@@ -63,10 +63,10 @@
       : count_(count), src_array_(src_array), offset_(offset), high_byte_(high_byte) {
   }
 
-  void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Avoid AsString as object is not yet in live bitmap or allocation stack.
-    String* string = down_cast<String*>(obj);
+    ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
     string->SetCount(count_);
     DCHECK(!string->IsCompressed() || kUseStringCompression);
     int32_t length = String::GetLengthFromCount(count_);
@@ -99,10 +99,10 @@
     count_(count), src_array_(src_array), offset_(offset) {
   }
 
-  void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Avoid AsString as object is not yet in live bitmap or allocation stack.
-    String* string = down_cast<String*>(obj);
+    ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
     string->SetCount(count_);
     const uint16_t* const src = src_array_->GetData() + offset_;
     const int32_t length = String::GetLengthFromCount(count_);
@@ -131,10 +131,10 @@
     count_(count), src_string_(src_string), offset_(offset) {
   }
 
-  void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+  void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Avoid AsString as object is not yet in live bitmap or allocation stack.
-    String* string = down_cast<String*>(obj);
+    ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
     string->SetCount(count_);
     const int32_t length = String::GetLengthFromCount(count_);
     bool compressible = kUseStringCompression && String::GetCompressionFlagFromCount(count_);
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 8e81bc9..1852956 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -254,7 +254,9 @@
   LOG(INFO) << "VMDebug infopoint " << id << " hit";
 }
 
-static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass,
+static jlong VMDebug_countInstancesOfClass(JNIEnv* env,
+                                           jclass,
+                                           jclass javaClass,
                                            jboolean countAssignable) {
   ScopedObjectAccess soa(env);
   gc::Heap* const heap = Runtime::Current()->GetHeap();
@@ -263,13 +265,16 @@
   if (c == nullptr) {
     return 0;
   }
-  std::vector<mirror::Class*> classes {c.Ptr()};
+  VariableSizedHandleScope hs(soa.Self());
+  std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)};
   uint64_t count = 0;
   heap->CountInstances(classes, countAssignable, &count);
   return count;
 }
 
-static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses,
+static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env,
+                                                  jclass,
+                                                  jobjectArray javaClasses,
                                                   jboolean countAssignable) {
   ScopedObjectAccess soa(env);
   gc::Heap* const heap = Runtime::Current()->GetHeap();
@@ -279,14 +284,15 @@
   if (decoded_classes == nullptr) {
     return nullptr;
   }
-  std::vector<mirror::Class*> classes;
+  VariableSizedHandleScope hs(soa.Self());
+  std::vector<Handle<mirror::Class>> classes;
   for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) {
-    classes.push_back(decoded_classes->Get(i));
+    classes.push_back(hs.NewHandle(decoded_classes->Get(i)));
   }
   std::vector<uint64_t> counts(classes.size(), 0u);
   // Heap::CountInstances can handle null and will put 0 for these classes.
   heap->CountInstances(classes, countAssignable, &counts[0]);
-  auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
+  ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
   if (long_counts == nullptr) {
     soa.Self()->AssertPendingOOMException();
     return nullptr;
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index c20c8b8..6b9468d 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -296,10 +296,10 @@
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map));
   if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
     os << prefix << "(backtrace::Unwind failed for thread " << tid
-       << ": " <<  backtrace->GetErrorString(backtrace->GetError()) << ")\n";
+       << ": " <<  backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl;
     return;
   } else if (backtrace->NumFrames() == 0) {
-    os << prefix << "(no native stack frames for thread " << tid << ")\n";
+    os << prefix << "(no native stack frames for thread " << tid << ")" << std::endl;
     return;
   }
 
@@ -354,7 +354,7 @@
       }
       os << ")";
     }
-    os << "\n";
+    os << std::endl;
     if (try_addr2line && use_addr2line) {
       Addr2line(it->map.name, it->pc - it->map.start, os, prefix, &addr2line_state);
     }
@@ -395,7 +395,7 @@
     if (include_count) {
       os << StringPrintf("#%02zd ", i);
     }
-    os << text << "\n";
+    os << text << std::endl;
   }
 }
 
diff --git a/runtime/obj_ptr-inl.h b/runtime/obj_ptr-inl.h
index f0a5f6f..d0be6dc 100644
--- a/runtime/obj_ptr-inl.h
+++ b/runtime/obj_ptr-inl.h
@@ -33,7 +33,7 @@
 template<class MirrorType, bool kPoison>
 inline void ObjPtr<MirrorType, kPoison>::AssertValid() const {
   if (kPoison) {
-    CHECK(IsValid()) << "Stale object pointer " << Ptr() << " , expected cookie "
+    CHECK(IsValid()) << "Stale object pointer " << PtrUnchecked() << " , expected cookie "
         << TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie();
   }
 }
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 2d9149a..de6683c 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -18,6 +18,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: ["events.cc",
+           "heap.cc",
            "object_tagging.cc",
            "OpenjdkJvmTi.cc",
            "transform.cc"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 36be2a0..05da585 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -39,6 +39,7 @@
 #include "art_jvmti.h"
 #include "base/mutex.h"
 #include "events-inl.h"
+#include "heap.h"
 #include "jni_env_ext-inl.h"
 #include "object_tagging.h"
 #include "obj_ptr-inl.h"
@@ -276,7 +277,8 @@
                                        jclass klass,
                                        const jvmtiHeapCallbacks* callbacks,
                                        const void* user_data) {
-    return ERR(NOT_IMPLEMENTED);
+    HeapUtil heap_util(&gObjectTagTable);
+    return heap_util.IterateThroughHeap(env, heap_filter, klass, callbacks, user_data);
   }
 
   static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) {
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 4d5b7e0..450f85e 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -128,11 +128,13 @@
  public:
   explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {}
 
-  void ObjectAllocated(art::Thread* self, art::mirror::Object** obj, size_t byte_count)
+  void ObjectAllocated(art::Thread* self, art::ObjPtr<art::mirror::Object>* obj, size_t byte_count)
       REQUIRES_SHARED(art::Locks::mutator_lock_) {
     DCHECK_EQ(self, art::Thread::Current());
 
     if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+      art::StackHandleScope<1> hs(self);
+      auto h = hs.NewHandleWrapper(obj);
       // jvmtiEventVMObjectAlloc parameters:
       //      jvmtiEnv *jvmti_env,
       //      JNIEnv* jni_env,
@@ -153,7 +155,7 @@
       ScopedLocalRef<jobject> object(
           jni_env, jni_env->AddLocalReference<jobject>(*obj));
       ScopedLocalRef<jclass> klass(
-          jni_env, jni_env->AddLocalReference<jclass>((*obj)->GetClass()));
+          jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
 
       handler_->DispatchEvent(self,
                               JVMTI_EVENT_VM_OBJECT_ALLOC,
diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/heap.cc
new file mode 100644
index 0000000..95d9a1d
--- /dev/null
+++ b/runtime/openjdkjvmti/heap.cc
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#include "heap.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "gc/heap.h"
+#include "mirror/class.h"
+#include "object_callbacks.h"
+#include "object_tagging.h"
+#include "obj_ptr-inl.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+struct IterateThroughHeapData {
+  IterateThroughHeapData(HeapUtil* _heap_util,
+                         jint heap_filter,
+                         art::ObjPtr<art::mirror::Class> klass,
+                         const jvmtiHeapCallbacks* _callbacks,
+                         const void* _user_data)
+      : heap_util(_heap_util),
+        filter_klass(klass),
+        callbacks(_callbacks),
+        user_data(_user_data),
+        filter_out_tagged((heap_filter & JVMTI_HEAP_FILTER_TAGGED) != 0),
+        filter_out_untagged((heap_filter & JVMTI_HEAP_FILTER_UNTAGGED) != 0),
+        filter_out_class_tagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_TAGGED) != 0),
+        filter_out_class_untagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) != 0),
+        any_filter(filter_out_tagged ||
+                   filter_out_untagged ||
+                   filter_out_class_tagged ||
+                   filter_out_class_untagged),
+        stop_reports(false) {
+  }
+
+  bool ShouldReportByHeapFilter(jlong tag, jlong class_tag) {
+    if (!any_filter) {
+      return true;
+    }
+
+    if ((tag == 0 && filter_out_untagged) || (tag != 0 && filter_out_tagged)) {
+      return false;
+    }
+
+    if ((class_tag == 0 && filter_out_class_untagged) ||
+        (class_tag != 0 && filter_out_class_tagged)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  HeapUtil* heap_util;
+  art::ObjPtr<art::mirror::Class> filter_klass;
+  const jvmtiHeapCallbacks* callbacks;
+  const void* user_data;
+  const bool filter_out_tagged;
+  const bool filter_out_untagged;
+  const bool filter_out_class_tagged;
+  const bool filter_out_class_untagged;
+  const bool any_filter;
+
+  bool stop_reports;
+};
+
+static void IterateThroughHeapObjectCallback(art::mirror::Object* obj, void* arg)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  IterateThroughHeapData* ithd = reinterpret_cast<IterateThroughHeapData*>(arg);
+  // Early return, as we can't really stop visiting.
+  if (ithd->stop_reports) {
+    return;
+  }
+
+  art::ScopedAssertNoThreadSuspension no_suspension("IterateThroughHeapCallback");
+
+  jlong tag = 0;
+  ithd->heap_util->GetTags()->GetTag(obj, &tag);
+
+  jlong class_tag = 0;
+  art::ObjPtr<art::mirror::Class> klass = obj->GetClass();
+  ithd->heap_util->GetTags()->GetTag(klass.Ptr(), &class_tag);
+  // For simplicity, even if we find a tag = 0, assume 0 = not tagged.
+
+  if (!ithd->ShouldReportByHeapFilter(tag, class_tag)) {
+    return;
+  }
+
+  // TODO: Handle array_primitive_value_callback.
+
+  if (ithd->filter_klass != nullptr) {
+    if (ithd->filter_klass != klass) {
+      return;
+    }
+  }
+
+  jlong size = obj->SizeOf();
+
+  jint length = -1;
+  if (obj->IsArrayInstance()) {
+    length = obj->AsArray()->GetLength();
+  }
+
+  jlong saved_tag = tag;
+  jint ret = ithd->callbacks->heap_iteration_callback(class_tag,
+                                                      size,
+                                                      &tag,
+                                                      length,
+                                                      const_cast<void*>(ithd->user_data));
+
+  if (tag != saved_tag) {
+    ithd->heap_util->GetTags()->Set(obj, tag);
+  }
+
+  ithd->stop_reports = (ret & JVMTI_VISIT_ABORT) != 0;
+
+  // TODO Implement array primitive and string primitive callback.
+  // TODO Implement primitive field callback.
+}
+
+jvmtiError HeapUtil::IterateThroughHeap(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                        jint heap_filter,
+                                        jclass klass,
+                                        const jvmtiHeapCallbacks* callbacks,
+                                        const void* user_data) {
+  if (callbacks == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  if (callbacks->array_primitive_value_callback != nullptr) {
+    // TODO: Implement.
+    return ERR(NOT_IMPLEMENTED);
+  }
+
+  art::Thread* self = art::Thread::Current();
+  art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
+
+  IterateThroughHeapData ithd(this,
+                              heap_filter,
+                              soa.Decode<art::mirror::Class>(klass),
+                              callbacks,
+                              user_data);
+
+  art::Runtime::Current()->GetHeap()->VisitObjects(IterateThroughHeapObjectCallback, &ithd);
+
+  return ERR(NONE);
+}
+
+}  // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/heap.h b/runtime/openjdkjvmti/heap.h
new file mode 100644
index 0000000..fb9a216
--- /dev/null
+++ b/runtime/openjdkjvmti/heap.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ObjectTagTable;
+
+class HeapUtil {
+ public:
+  explicit HeapUtil(ObjectTagTable* tags) : tags_(tags) {
+  }
+
+  jvmtiError IterateThroughHeap(jvmtiEnv* env,
+                                jint heap_filter,
+                                jclass klass,
+                                const jvmtiHeapCallbacks* callbacks,
+                                const void* user_data);
+
+  ObjectTagTable* GetTags() {
+    return tags_;
+  }
+
+ private:
+  ObjectTagTable* tags_;
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index bb17cac..29d4830 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -87,6 +87,33 @@
   return false;
 }
 
+bool ObjectTagTable::Set(art::mirror::Object* obj, jlong new_tag) {
+  art::Thread* self = art::Thread::Current();
+  art::MutexLock mu(self, allow_disallow_lock_);
+  Wait(self);
+
+  for (auto& pair : tagged_objects_) {
+    if (pair.first.Read(nullptr) == obj) {
+      pair.second = new_tag;
+      return true;
+    }
+  }
+
+  // TODO refactor with Add.
+  if (first_free_ == tagged_objects_.size()) {
+    tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), new_tag));
+    first_free_++;
+  } else {
+    DCHECK_LT(first_free_, tagged_objects_.size());
+    DCHECK(tagged_objects_[first_free_].first.IsNull());
+    tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), new_tag);
+    // TODO: scan for free elements.
+    first_free_ = tagged_objects_.size();
+  }
+
+  return false;
+}
+
 void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
   if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
     SweepImpl<true>(visitor);
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
index 45f3e4f..b399e65 100644
--- a/runtime/openjdkjvmti/object_tagging.h
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -42,6 +42,10 @@
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_);
 
+  bool Set(art::mirror::Object* obj, jlong tag)
+      REQUIRES_SHARED(art::Locks::mutator_lock_)
+      REQUIRES(!allow_disallow_lock_);
+
   bool GetTag(art::mirror::Object* obj, jlong* result)
       REQUIRES_SHARED(art::Locks::mutator_lock_)
       REQUIRES(!allow_disallow_lock_) {
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index e0ba8e7..a9f39d0 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -41,7 +41,7 @@
 
 void ReferenceTable::Add(ObjPtr<mirror::Object> obj) {
   DCHECK(obj != nullptr);
-  VerifyObject(obj.Ptr());
+  VerifyObject(obj);
   if (entries_.size() >= max_size_) {
     LOG(FATAL) << "ReferenceTable '" << name_ << "' "
                << "overflowed (" << max_size_ << " entries)";
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index aed6a2b..be97860 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -27,7 +27,6 @@
 
 namespace art {
 
-static constexpr bool kDumpHeapObjectOnSigsevg = false;
 static constexpr bool kUseSignalHandler = false;
 
 struct sigaction old_action;
@@ -48,11 +47,6 @@
   if (runtime != nullptr) {
     // Print this out first in case DumpObject faults.
     LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
-    gc::Heap* heap = runtime->GetHeap();
-    if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
-      LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: ";
-      heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr));
-    }
   }
   // Run the old signal handler.
   old_action.sa_sigaction(signal_number, info, raw_context);
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index cee73e1..93704a9 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -21,6 +21,7 @@
 #include <sys/utsname.h>
 #include <inttypes.h>
 
+#include <iostream>
 #include <sstream>
 
 #include "base/dumpable.h"
@@ -35,7 +36,6 @@
 
 namespace art {
 
-static constexpr bool kDumpHeapObjectOnSigsevg = false;
 static constexpr bool kUseSigRTTimeout = true;
 static constexpr bool kDumpNativeStackOnTimeout = true;
 
@@ -337,17 +337,21 @@
   UContext thread_context(raw_context);
   Backtrace thread_backtrace(raw_context);
 
-  LOG(FATAL_WITHOUT_ABORT) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
-                      << StringPrintf("Fatal signal %d (%s), code %d (%s)",
-                                      signal_number, GetSignalName(signal_number),
-                                      info->si_code,
-                                      GetSignalCodeName(signal_number, info->si_code))
-                      << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << "\n"
-                      << "OS: " << Dumpable<OsInfo>(os_info) << "\n"
-                      << "Cmdline: " << cmd_line << "\n"
-                      << "Thread: " << tid << " \"" << thread_name << "\"\n"
-                      << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
-                      << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
+  // Note: We are using cerr directly instead of LOG macros to ensure even just partial output
+  //       makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable
+  //       considering this is an abort situation.
+
+  std::cerr << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
+            << StringPrintf("Fatal signal %d (%s), code %d (%s)",
+                            signal_number, GetSignalName(signal_number),
+                            info->si_code,
+                            GetSignalCodeName(signal_number, info->si_code))
+            << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << std::endl
+            << "OS: " << Dumpable<OsInfo>(os_info) << std::endl
+            << "Cmdline: " << cmd_line << std::endl
+            << "Thread: " << tid << " \"" << thread_name << "\"" << std::endl
+            << "Registers:\n" << Dumpable<UContext>(thread_context) << std::endl
+            << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << std::endl;
   if (kIsDebugBuild && signal_number == SIGSEGV) {
     PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
   }
@@ -357,23 +361,20 @@
       // Special timeout signal. Try to dump all threads.
       // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
       //       are of value here.
-      runtime->GetThreadList()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), kDumpNativeStackOnTimeout);
+      runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout);
+      std::cerr << std::endl;
     }
-    gc::Heap* heap = runtime->GetHeap();
-    LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
-    if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
-      LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: ";
-      heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr));
-    }
+    std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl;
   }
   if (getenv("debug_db_uid") != nullptr || getenv("art_wait_for_gdb_on_crash") != nullptr) {
-    LOG(FATAL_WITHOUT_ABORT) << "********************************************************\n"
-                        << "* Process " << getpid() << " thread " << tid << " \"" << thread_name
-                        << "\""
-                        << " has been suspended while crashing.\n"
-                        << "* Attach gdb:\n"
-                        << "*     gdb -p " << tid << "\n"
-                        << "********************************************************\n";
+    std::cerr << "********************************************************\n"
+              << "* Process " << getpid() << " thread " << tid << " \"" << thread_name
+              << "\""
+              << " has been suspended while crashing.\n"
+              << "* Attach gdb:\n"
+              << "*     gdb -p " << tid << "\n"
+              << "********************************************************"
+              << std::endl;
     // Wait for debugger to attach.
     while (true) {
     }
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3e2ecfe..45d3e34 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1826,7 +1826,7 @@
 
 size_t Thread::NumHandleReferences() {
   size_t count = 0;
-  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
+  for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
     count += cur->NumberOfReferences();
   }
   return count;
@@ -1835,7 +1835,7 @@
 bool Thread::HandleScopeContains(jobject obj) const {
   StackReference<mirror::Object>* hs_entry =
       reinterpret_cast<StackReference<mirror::Object>*>(obj);
-  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) {
+  for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) {
     if (cur->Contains(hs_entry)) {
       return true;
     }
@@ -1847,12 +1847,8 @@
 void Thread::HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id) {
   BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
       visitor, RootInfo(kRootNativeStack, thread_id));
-  for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
-    for (size_t j = 0, count = cur->NumberOfReferences(); j < count; ++j) {
-      // GetReference returns a pointer to the stack reference within the handle scope. If this
-      // needs to be updated, it will be done by the root visitor.
-      buffered_visitor.VisitRootIfNonNull(cur->GetHandle(j).GetReference());
-    }
+  for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
+    cur->VisitRoots(buffered_visitor);
   }
 }
 
@@ -1875,7 +1871,7 @@
     if (LIKELY(HandleScopeContains(obj))) {
       // Read from handle scope.
       result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr();
-      VerifyObject(result.Ptr());
+      VerifyObject(result);
     } else {
       tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj);
       expect_null = true;
diff --git a/runtime/thread.h b/runtime/thread.h
index 20b4cc1..376a69c 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -799,17 +799,17 @@
   void HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  HandleScope* GetTopHandleScope() {
+  BaseHandleScope* GetTopHandleScope() {
     return tlsPtr_.top_handle_scope;
   }
 
-  void PushHandleScope(HandleScope* handle_scope) {
+  void PushHandleScope(BaseHandleScope* handle_scope) {
     DCHECK_EQ(handle_scope->GetLink(), tlsPtr_.top_handle_scope);
     tlsPtr_.top_handle_scope = handle_scope;
   }
 
-  HandleScope* PopHandleScope() {
-    HandleScope* handle_scope = tlsPtr_.top_handle_scope;
+  BaseHandleScope* PopHandleScope() {
+    BaseHandleScope* handle_scope = tlsPtr_.top_handle_scope;
     DCHECK(handle_scope != nullptr);
     tlsPtr_.top_handle_scope = tlsPtr_.top_handle_scope->GetLink();
     return handle_scope;
@@ -1446,7 +1446,7 @@
     mirror::Object* monitor_enter_object;
 
     // Top of linked list of handle scopes or null for none.
-    HandleScope* top_handle_scope;
+    BaseHandleScope* top_handle_scope;
 
     // Needed to get the right ClassLoader in JNI_OnLoad, but also
     // useful for testing.
diff --git a/runtime/verify_object-inl.h b/runtime/verify_object-inl.h
index 4892b49..43151dd 100644
--- a/runtime/verify_object-inl.h
+++ b/runtime/verify_object-inl.h
@@ -29,7 +29,7 @@
   if (kVerifyObjectSupport > kVerifyObjectModeDisabled && obj != nullptr) {
     if (kVerifyObjectSupport > kVerifyObjectModeFast) {
       // Slow object verification, try the heap right away.
-      Runtime::Current()->GetHeap()->VerifyObjectBody(obj.Ptr());
+      Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
     } else {
       // Fast object verification, only call the heap if our quick sanity tests fail. The heap will
       // print the diagnostic message.
@@ -40,7 +40,7 @@
         failed = failed || !VerifyClassClass(c);
       }
       if (UNLIKELY(failed)) {
-        Runtime::Current()->GetHeap()->VerifyObjectBody(obj.Ptr());
+        Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
       }
     }
   }
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 7acf008..dca00bd 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -111,6 +111,24 @@
     return result;
   }
 
+  /// CHECK-START: int Main.periodicXorSequence(int) BCE (before)
+  /// CHECK-DAG: BoundsCheck
+  //
+  /// CHECK-START: int Main.periodicXorSequence(int) BCE (after)
+  /// CHECK-NOT: BoundsCheck
+  /// CHECK-NOT: Deoptimize
+  private static int periodicXorSequence(int tc) {
+    int[] x = { 1, 3 };
+    // Loop with periodic sequence (0, 1).
+    int k = 0;
+    int result = 0;
+    for (int i = 0; i < tc; i++) {
+      result += x[k];
+      k ^= 1;
+    }
+    return result;
+  }
+
   /// CHECK-START: int Main.justRightUp1() BCE (before)
   /// CHECK-DAG: BoundsCheck
   //
@@ -895,8 +913,9 @@
     expectEquals(0, periodicIdiom(-1));
     for (int tc = 0; tc < 32; tc++) {
       int expected = (tc >> 1) << 2;
-      if ((tc & 1) != 0)
+      if ((tc & 1) != 0) {
         expected += 1;
+      }
       expectEquals(expected, periodicIdiom(tc));
     }
 
@@ -904,8 +923,9 @@
     expectEquals(0, periodicSequence2(-1));
     for (int tc = 0; tc < 32; tc++) {
       int expected = (tc >> 1) << 2;
-      if ((tc & 1) != 0)
+      if ((tc & 1) != 0) {
         expected += 1;
+      }
       expectEquals(expected, periodicSequence2(tc));
     }
 
@@ -915,6 +935,16 @@
       expectEquals(tc * 16, periodicSequence4(tc));
     }
 
+    // Periodic adds (1, 3), one at the time.
+    expectEquals(0, periodicXorSequence(-1));
+    for (int tc = 0; tc < 32; tc++) {
+      int expected = (tc >> 1) << 2;
+      if ((tc & 1) != 0) {
+        expected += 1;
+      }
+      expectEquals(expected, periodicXorSequence(tc));
+    }
+
     // Large bounds.
     expectEquals(55, justRightUp1());
     expectEquals(55, justRightUp2());
diff --git a/test/906-iterate-heap/build b/test/906-iterate-heap/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/906-iterate-heap/build
@@ -0,0 +1,17 @@
+#!/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-build "$@" --experimental agents
diff --git a/test/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt
new file mode 100644
index 0000000..72cd47d
--- /dev/null
+++ b/test/906-iterate-heap/expected.txt
@@ -0,0 +1,2 @@
+[{tag=1, class-tag=0, size=8, length=-1}, {tag=2, class-tag=100, size=8, length=-1}, {tag=3, class-tag=100, size=8, length=-1}, {tag=4, class-tag=0, size=32, length=5}, {tag=100, class-tag=0, size=<class>, length=-1}]
+[{tag=11, class-tag=0, size=8, length=-1}, {tag=12, class-tag=110, size=8, length=-1}, {tag=13, class-tag=110, size=8, length=-1}, {tag=14, class-tag=0, size=32, length=5}, {tag=110, class-tag=0, size=<class>, length=-1}]
diff --git a/test/906-iterate-heap/info.txt b/test/906-iterate-heap/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/906-iterate-heap/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
new file mode 100644
index 0000000..ab1d8d8
--- /dev/null
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "iterate_heap.h"
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedPrimitiveArray.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test906IterateHeap {
+
+class IterationConfig {
+ public:
+  IterationConfig() {}
+  virtual ~IterationConfig() {}
+
+  virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
+};
+
+static jint JNICALL HeapIterationCallback(jlong class_tag,
+                                          jlong size,
+                                          jlong* tag_ptr,
+                                          jint length,
+                                          void* user_data) {
+  IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data);
+  return config->Handle(class_tag, size, tag_ptr, length);
+}
+
+static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config) {
+  jvmtiHeapCallbacks callbacks;
+  memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+  callbacks.heap_iteration_callback = HeapIterationCallback;
+
+  jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter,
+                                                 klass_filter,
+                                                 &callbacks,
+                                                 config);
+  if (ret != JVMTI_ERROR_NONE) {
+    char* err;
+    jvmti_env->GetErrorName(ret, &err);
+    printf("Failure running IterateThroughHeap: %s\n", err);
+    return false;
+  }
+  return true;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                                    jclass klass ATTRIBUTE_UNUSED,
+                                                                    jint heap_filter,
+                                                                    jclass klass_filter,
+                                                                    jint stop_after) {
+  class CountIterationConfig : public IterationConfig {
+   public:
+    CountIterationConfig(jint _counter, jint _stop_after)
+        : counter(_counter),
+          stop_after(_stop_after) {
+    }
+
+    jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
+                jlong size ATTRIBUTE_UNUSED,
+                jlong* tag_ptr ATTRIBUTE_UNUSED,
+                jint length ATTRIBUTE_UNUSED) OVERRIDE {
+      counter++;
+      if (counter == stop_after) {
+        return JVMTI_VISIT_ABORT;
+      }
+      return 0;
+    }
+
+    jint counter;
+    const jint stop_after;
+  };
+
+  CountIterationConfig config(0, stop_after);
+  Run(heap_filter, klass_filter, &config);
+
+  if (config.counter > config.stop_after) {
+    printf("Error: more objects visited than signaled.");
+  }
+
+  return config.counter;
+}
+
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapData(JNIEnv* env,
+                                                                   jclass klass ATTRIBUTE_UNUSED,
+                                                                   jint heap_filter,
+                                                                   jclass klass_filter,
+                                                                   jlongArray class_tags,
+                                                                   jlongArray sizes,
+                                                                   jlongArray tags,
+                                                                   jintArray lengths) {
+  class DataIterationConfig : public IterationConfig {
+   public:
+    jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) OVERRIDE {
+      class_tags_.push_back(class_tag);
+      sizes_.push_back(size);
+      tags_.push_back(*tag_ptr);
+      lengths_.push_back(length);
+
+      return 0;  // Continue.
+    }
+
+    std::vector<jlong> class_tags_;
+    std::vector<jlong> sizes_;
+    std::vector<jlong> tags_;
+    std::vector<jint> lengths_;
+  };
+
+  DataIterationConfig config;
+  if (!Run(heap_filter, klass_filter, &config)) {
+    return -1;
+  }
+
+  ScopedLongArrayRW s_class_tags(env, class_tags);
+  ScopedLongArrayRW s_sizes(env, sizes);
+  ScopedLongArrayRW s_tags(env, tags);
+  ScopedIntArrayRW s_lengths(env, lengths);
+
+  for (size_t i = 0; i != config.class_tags_.size(); ++i) {
+    s_class_tags[i] = config.class_tags_[i];
+    s_sizes[i] = config.sizes_[i];
+    s_tags[i] = config.tags_[i];
+    s_lengths[i] = config.lengths_[i];
+  }
+
+  return static_cast<jint>(config.class_tags_.size());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                                  jclass klass ATTRIBUTE_UNUSED,
+                                                                  jint heap_filter,
+                                                                  jclass klass_filter) {
+  class AddIterationConfig : public IterationConfig {
+   public:
+    AddIterationConfig() {}
+
+    jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
+                jlong size ATTRIBUTE_UNUSED,
+                jlong* tag_ptr,
+                jint length ATTRIBUTE_UNUSED) OVERRIDE {
+      jlong current_tag = *tag_ptr;
+      if (current_tag != 0) {
+        *tag_ptr = current_tag + 10;
+      }
+      return 0;
+    }
+  };
+
+  AddIterationConfig config;
+  Run(heap_filter, klass_filter, &config);
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+            char* options ATTRIBUTE_UNUSED,
+            void* reserved ATTRIBUTE_UNUSED) {
+  if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+    printf("Unable to get jvmti env!\n");
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace Test906IterateHeap
+}  // namespace art
diff --git a/test/906-iterate-heap/iterate_heap.h b/test/906-iterate-heap/iterate_heap.h
new file mode 100644
index 0000000..f25cdba
--- /dev/null
+++ b/test/906-iterate-heap/iterate_heap.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
+#define ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test906IterateHeap {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+}  // namespace Test906IterateHeap
+}  // namespace art
+
+#endif  // ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run
new file mode 100755
index 0000000..3e135a3
--- /dev/null
+++ b/test/906-iterate-heap/run
@@ -0,0 +1,43 @@
+#!/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.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if  [[ "$@" == *"-O"* ]]; then
+  agent=libtiagent.so
+  plugin=libopenjdkjvmti.so
+  lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+  arg="jvm"
+else
+  arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+  other_args=" -Xcompiler-option --debuggable "
+else
+  other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+                   --experimental runtime-plugins \
+                   --runtime-option -agentpath:${agent}=906-iterate-heap,${arg} \
+                   --android-runtime-option -Xplugin:${plugin} \
+                   ${other_args} \
+                   --args ${lib}
diff --git a/test/906-iterate-heap/src/Main.java b/test/906-iterate-heap/src/Main.java
new file mode 100644
index 0000000..544a365
--- /dev/null
+++ b/test/906-iterate-heap/src/Main.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class Main {
+  public static void main(String[] args) throws Exception {
+    System.loadLibrary(args[1]);
+
+    doTest();
+  }
+
+  public static void doTest() throws Exception {
+    A a = new A();
+    B b = new B();
+    B b2 = new B();
+    C c = new C();
+    A[] aArray = new A[5];
+
+    setTag(a, 1);
+    setTag(b, 2);
+    setTag(b2, 3);
+    setTag(aArray, 4);
+    setTag(B.class, 100);
+
+    int all = iterateThroughHeapCount(0, null, Integer.MAX_VALUE);
+    int tagged = iterateThroughHeapCount(HEAP_FILTER_OUT_UNTAGGED, null, Integer.MAX_VALUE);
+    int untagged = iterateThroughHeapCount(HEAP_FILTER_OUT_TAGGED, null, Integer.MAX_VALUE);
+    int taggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_UNTAGGED, null,
+        Integer.MAX_VALUE);
+    int untaggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_TAGGED, null,
+        Integer.MAX_VALUE);
+
+    if (all != tagged + untagged) {
+      throw new IllegalStateException("Instances: " + all + " != " + tagged + " + " + untagged);
+    }
+    if (all != taggedClass + untaggedClass) {
+      throw new IllegalStateException("By class: " + all + " != " + taggedClass + " + " +
+          untaggedClass);
+    }
+    if (tagged != 5) {
+      throw new IllegalStateException(tagged + " tagged objects");
+    }
+    if (taggedClass != 2) {
+      throw new IllegalStateException(tagged + " objects with tagged class");
+    }
+    if (all == tagged) {
+      throw new IllegalStateException("All objects tagged");
+    }
+    if (all == taggedClass) {
+      throw new IllegalStateException("All objects have tagged class");
+    }
+
+    long classTags[] = new long[100];
+    long sizes[] = new long[100];
+    long tags[] = new long[100];
+    int lengths[] = new int[100];
+
+    int n = iterateThroughHeapData(HEAP_FILTER_OUT_UNTAGGED, null, classTags, sizes, tags, lengths);
+    System.out.println(sort(n, classTags, sizes, tags, lengths));
+
+    iterateThroughHeapAdd(HEAP_FILTER_OUT_UNTAGGED, null);
+    n = iterateThroughHeapData(HEAP_FILTER_OUT_UNTAGGED, null, classTags, sizes, tags, lengths);
+    System.out.println(sort(n, classTags, sizes, tags, lengths));
+  }
+
+  static class A {
+  }
+
+  static class B {
+  }
+
+  static class C {
+  }
+
+  static class HeapElem implements Comparable<HeapElem> {
+    long classTag;
+    long size;
+    long tag;
+    int length;
+
+    public int compareTo(HeapElem other) {
+      if (tag != other.tag) {
+        return Long.compare(tag, other.tag);
+      }
+      if (classTag != other.classTag) {
+        return Long.compare(classTag, other.classTag);
+      }
+      if (size != other.size) {
+        return Long.compare(size, other.size);
+      }
+      return Integer.compare(length, other.length);
+    }
+
+    public String toString() {
+      return "{tag=" + tag + ", class-tag=" + classTag + ", size=" +
+          (tag >= 100 ? "<class>" : size)  // Class size is dependent on 32-bit vs 64-bit,
+                                           // so strip it.
+          + ", length=" + length + "}";
+    }
+  }
+
+  private static ArrayList<HeapElem> sort(int n, long classTags[], long sizes[], long tags[],
+      int lengths[]) {
+    ArrayList<HeapElem> ret = new ArrayList<HeapElem>(n);
+    for (int i = 0; i < n; i++) {
+      HeapElem elem = new HeapElem();
+      elem.classTag = classTags[i];
+      elem.size = sizes[i];
+      elem.tag = tags[i];
+      elem.length = lengths[i];
+      ret.add(elem);
+    }
+    Collections.sort(ret);
+    return ret;
+  }
+
+  private static native void setTag(Object o, long tag);
+  private static native long getTag(Object o);
+
+  private final static int HEAP_FILTER_OUT_TAGGED = 0x4;
+  private final static int HEAP_FILTER_OUT_UNTAGGED = 0x8;
+  private final static int HEAP_FILTER_OUT_CLASS_TAGGED = 0x10;
+  private final static int HEAP_FILTER_OUT_CLASS_UNTAGGED = 0x20;
+
+  private static native int iterateThroughHeapCount(int heapFilter,
+      Class<?> klassFilter, int stopAfter);
+  private static native int iterateThroughHeapData(int heapFilter,
+      Class<?> klassFilter, long classTags[], long sizes[], long tags[], int lengths[]);
+  private static native int iterateThroughHeapAdd(int heapFilter,
+      Class<?> klassFilter);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 4457e8a..45673f5 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -238,8 +238,8 @@
     shared_libs: ["libartd"],
 }
 
-art_cc_test_library {
-    name: "libtiagent",
+art_cc_defaults {
+    name: "libtiagent-defaults",
     defaults: ["libartagent-defaults"],
     srcs: [
         "ti-agent/common_load.cc",
@@ -248,10 +248,18 @@
         "903-hello-tagging/tagging.cc",
         "904-object-allocation/tracking.cc",
         "905-object-free/tracking_free.cc",
+        "906-iterate-heap/iterate_heap.cc",
     ],
     shared_libs: [
-        "libart",
         "libbase",
+    ],
+}
+
+art_cc_test_library {
+    name: "libtiagent",
+    defaults: ["libtiagent-defaults"],
+    shared_libs: [
+        "libart",
         "libopenjdkjvmti",
     ],
 }
@@ -259,20 +267,11 @@
 art_cc_test_library {
     name: "libtiagentd",
     defaults: [
-        "libartagent-defaults",
+        "libtiagent-defaults",
         "art_debug_defaults",
     ],
-    srcs: [
-        "ti-agent/common_load.cc",
-        "901-hello-ti-agent/basics.cc",
-        "902-hello-transformation/transform.cc",
-        "903-hello-tagging/tagging.cc",
-        "904-object-allocation/tracking.cc",
-        "905-object-free/tracking_free.cc",
-    ],
     shared_libs: [
         "libartd",
-        "libbase",
         "libopenjdkjvmtid",
     ],
 }
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 29000f0..64ff5ba 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -263,7 +263,7 @@
 # 147-stripped-dex-fallback isn't supported on device because --strip-dex
 # requires the zip command.
 # 569-checker-pattern-replacement tests behaviour present only on host.
-# 90{2,3,4,5} are not supported in current form due to linker
+# 90{2,3,4,5,6} are not supported in current form due to linker
 # restrictions. See b/31681198
 TEST_ART_BROKEN_TARGET_TESTS := \
   147-stripped-dex-fallback \
@@ -272,6 +272,7 @@
   903-hello-tagging \
   904-object-allocation \
   905-object-free \
+  906-iterate-heap \
 
 ifneq (,$(filter target,$(TARGET_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -487,8 +488,11 @@
 # Known broken tests for the JIT.
 # CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT
 # also uses Generic JNI instead of the JNI compiler.
+# Test 906 iterates the heap filtering with different options. No instances should be created
+# between those runs to be able to have precise checks.
 TEST_ART_BROKEN_JIT_RUN_TESTS := \
-  137-cfi
+  137-cfi \
+  906-iterate-heap \
 
 ifneq (,$(filter jit,$(COMPILER_TYPES)))
   ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 2b8947e..c412636 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -29,6 +29,7 @@
 #include "903-hello-tagging/tagging.h"
 #include "904-object-allocation/tracking.h"
 #include "905-object-free/tracking_free.h"
+#include "906-iterate-heap/iterate_heap.h"
 
 namespace art {
 
@@ -50,6 +51,7 @@
   { "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr },
   { "904-object-allocation", Test904ObjectAllocation::OnLoad, nullptr },
   { "905-object-free", Test905ObjectFree::OnLoad, nullptr },
+  { "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr },
 };
 
 static AgentLib* FindAgent(char* name) {